Author SHA1 Message Date
jetthuangai 3c053e09df Bump version 1.7.0 2026-10-18 12:10:29 +07:00
jetthuangai 5a617560a3 docs: SAM 3 and NH_SEGS nodes in the EN/VI guides
- vision guide (EN/VI): requirements and install, how the two passes work
  and their limits, the NH_SEGS type, the six nodes, three workflows and
  troubleshooting
- READMEs (75 nodes), CHANGELOG 1.7.0, THIRD_PARTY_NOTICES (SAM License,
  user-installed sam3 and checkpoint)
- six node cards, sam3-segment.jpg example from the public-domain astronaut
  photo; build_examples.py builds it with --gpu
2026-10-18 12:10:18 +07:00
jetthuangai 5389e93994 feat: SAM 3 segmentation and NH_SEGS instance nodes (Vision v2 P0)
Six new Vision nodes, 69 -> 75:
- SAM3 Segment: every instance of the concepts you name, with Meta's SAM 3.
  The whole image is encoded once for all concepts (pass 1); in balanced mode
  each instance is segmented again on a full-resolution close-up with the
  box-prompt decoder (pass 2), so small and medium objects keep crisp edges
  on 4K-8K photos. A close-up that does not show the pass-1 instance is
  dropped (tolerant check that keeps thin poles and cables).
- Segs Select, Segs Compose (label formulas: | - & ~ all image none),
  Segs To Masks, Segs To Impact SEGS, Segs Preview.

NH_SEGS keeps per-instance masks only inside each box, so dozens of
instances on an 8K image stay small; full-size masks are built only on
output (with a RAM guard in Segs To Masks).

Self-contained: no other custom node pack is used. Meta's sam3 package is
imported lazily with an API check (install hint pinned to the tested
commit), TF32 switches changed by its import are restored, and the gated
sam3.pt checkpoint in models/sam3 is checked before loading and never
downloaded. CUDA only. Weights and sam3's GPU-side tables go back to RAM
after each run unless keep_model_loaded.
2026-10-18 12:10:18 +07:00
jetthuangai f4843d30d3 docs: bilingual EN/VI guide, node cards, examples and new README
- README rewritten: banner, highlights with real outputs, install incl.
  ONNX Runtime and extra_model_paths keys, quick start, full catalog,
  licence summary, PDF guide links
- docs/en and docs/vi: guide home + 13 pages covering all 69 nodes (card,
  inputs/outputs from the live definitions, behaviour, recipes, tips)
- docs/assets: 69 node cards generated from INPUT_TYPES, 14 example images
  and a banner rendered from real node outputs on public-domain/CC0 photos
- docs/_tools: card, example, spec and PDF builders (reportlab) so the docs
  can be regenerated from the code
2026-10-18 09:17:48 +07:00
135 changed files with 18039 additions and 326 deletions
+124 -319
View File
@@ -1,354 +1,159 @@
<p align="center"><img src="docs/assets/banner.jpg" alt="NH-Nodes — image, mask, resolution and workflow nodes for ComfyUI" width="100%"></p>
<p align="center">
<a href="https://registry.comfy.org/nodes/nh-nodes"><img src="https://img.shields.io/badge/ComfyUI_Registry-nh--nodes-2563eb" alt="ComfyUI Registry"></a>
<a href="https://github.com/jetthuangai/NH-Nodes/actions/workflows/tests.yml"><img src="https://github.com/jetthuangai/NH-Nodes/actions/workflows/tests.yml/badge.svg" alt="Tests"></a>
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT_(see_notices)-16a34a" alt="License"></a>
<a href="docs/en/README.md"><img src="https://img.shields.io/badge/Docs-EN_%7C_VI-0f766e" alt="Documentation"></a>
</p>
<p align="center"><b>English</b> · <a href="docs/vi/README.md">Tiếng Việt</a></p>
# NH-Nodes for ComfyUI
Production-focused custom nodes for ComfyUI workflows: image loading, tiling,
compositing, mask utilities, smart resize, logic routing, prompt/text tooling,
batch helpers, indexed model loaders, garment/portrait segmentation, and VTON
preprocessing.
**75 production-focused nodes** for ComfyUI: load and save image sets with their metadata, tile and composite large
images, pick the right resolution for current models and put results back into the original frame, refine masks,
segment garments and faces, find every person, window or sky by name with SAM 3, build VTON masks, route workflows
with lazy switches, generate prompts, and test many LoRAs or models from one node.
[![ComfyUI Registry](https://img.shields.io/badge/ComfyUI_Registry-nh--nodes-2563eb)](https://registry.comfy.org/nodes/nh-nodes)
[![GitHub](https://img.shields.io/badge/GitHub-jetthuangai%2FNH--Nodes-111827)](https://github.com/jetthuangai/NH-Nodes)
[![License: MIT](https://img.shields.io/badge/License-MIT-16a34a.svg)](LICENSE)
Every node is documented with a visual card, its inputs and outputs, and examples — in
[English](docs/en/README.md) and [Vietnamese](docs/vi/README.md).
- **Author:** [jetthuang.com](https://jetthuang.com)
- **Hugging Face:** [nhathoangfoto](https://huggingface.co/nhathoangfoto)
- **Support:** [PayPal](https://paypal.me/nhathoangfoto)
## Highlights
---
## Why NH-Nodes
NH-Nodes is built for practical workflow construction. It focuses on reusable
building blocks that remove repetitive graph work:
- Load images with filename/path metadata.
- Split images into tiles, upscale or process tiles, then stitch them back.
- Build image grids, layer composites, comparisons, and labeled previews.
- Resize images and masks with predictable aspect-ratio behavior.
- Route workflow branches with boolean logic and generic data switches.
- Generate prompts from templates, regex, lists, schedules, and text utilities.
- Pick indexed LoRA/diffusion models for larger configurable workflows.
- Prepare VTON masks, agnostic images, DensePose/parsing outputs, and related masks.
---
<table>
<tr>
<td width="50%"><a href="docs/en/resolution.md"><img src="docs/assets/examples/resize-modes.jpg" alt="Resize modes"></a><br>
<b>Resolution done right</b> — presets for FLUX.2, Qwen-Image and Z-Image, cover / contain / stretch resizing, and an
exact round trip back to the original frame.</td>
<td width="50%"><a href="docs/en/image-compose.md"><img src="docs/assets/examples/tile-untile.jpg" alt="Tile and untile"></a><br>
<b>Large images</b> — split into overlapping tiles, process them, and blend them back seamlessly; deep-zoom preview
for images too big for the canvas.</td>
</tr>
<tr>
<td><a href="docs/en/vision.md"><img src="docs/assets/examples/garment-segment.jpg" alt="Garment segmentation"></a><br>
<b>Segmentation</b> — name what you want (people, windows, the sky) and SAM 3 returns every instance as its own
mask, with crisp edges on small and medium objects even in 4K–8K photos; garment and face-part masks from
SegFormer models; VTON garment masks with pose and parsing.</td>
<td><a href="docs/en/mask.md"><img src="docs/assets/examples/mask-morphology.jpg" alt="Mask morphology"></a><br>
<b>Mask tools</b> — fast grow / shrink on the CPU or the GPU, hole filling, soft edges, bounding boxes and
aspect-ratio matching.</td>
</tr>
<tr>
<td><a href="docs/en/image-compose.md"><img src="docs/assets/examples/layer-layout.jpg" alt="Layer layout"></a><br>
<b>Compositing</b> — layouts, layer stacks with alpha, labelled grids and side-by-side comparisons.</td>
<td><a href="docs/en/logic.md"><img src="docs/assets/examples/grid-composite.jpg" alt="Grid composite"></a><br>
<b>Workflow logic</b> — lazy If/Else and switches that run only the chosen branch, safe math, counters, lists and
prompt tools.</td>
</tr>
</table>
## Installation
### ComfyUI Manager
**ComfyUI Manager** — search for `NH-Nodes`, install, restart ComfyUI.
Search for `NH-Nodes`, install, then restart ComfyUI.
### Comfy CLI
**Comfy CLI**
```bash
comfy node install nh-nodes
```
### Manual
**Manual**
```bash
cd ComfyUI/custom_nodes
git clone https://github.com/jetthuangai/NH-Nodes.git
cd NH-Nodes
pip install -r requirements.txt
python -m pip install -r requirements.txt
```
Restart ComfyUI after installation.
Restart ComfyUI afterwards. Requirements: a recent ComfyUI (tested with 0.41) and Python 3.10+. **SAM3 Segment** has
more requirements, listed below.
---
### Optional: VTON and segmentation models
## Featured Workflows
- **VTON Ultimate Processor** needs ONNX Runtime, which is not installed automatically (the CPU wheel would overwrite an
existing GPU build). Install one of them into ComfyUI's Python:
`python -m pip install onnxruntime-gpu` (NVIDIA) or `python -m pip install onnxruntime` (CPU).
- **Garment Segment**, **Portrait Segment** and **VTON** download their models from Hugging Face on first use (pinned
revisions, only the needed files). To keep models elsewhere, add these keys to `extra_model_paths.yaml`:
### Tile, Process, Untile
```yaml
my_models:
base_path: D:/ai-models/
nh_nodes: NH-Nodes/ # SegFormer models (garment, face parsing)
vton_mask: ComfyUI-Vton-Mask/ # VTON pose + parsing ONNX models
sam3: sam3/ # SAM 3 checkpoint (you download sam3.pt yourself)
```
- **Requirements for SAM 3** (SAM3 Segment only): an NVIDIA GPU with CUDA, Meta's `sam3` package and the `sam3.pt`
checkpoint. NH-Nodes installs and downloads none of them: you install the package yourself and request the weights
from Meta. The steps, and what to do if another SAM 3 pack is already installed, are in
[Before you start with SAM 3](docs/en/vision.md#before-you-start-with-sam-3).
Use this when an image is too large for a model, or when every tile needs the
same enhancement/upscale step.
## Quick start
```text
Load Image -> Image Tile (NH) -> upscale/process tiles -> Image Untile (NH)
```
| Goal | Chain | Guide |
|---|---|---|
| Upscale or refine a huge image piece by piece | Load Image → **Image Tile** → your sampler/upscaler per tile → **Image Untile** | [image-compose](docs/en/image-compose.md#image-tile-nh) |
| Generate at the model's native size, then restore the original framing | Load Image → **NH Smart Ratio Image Resize** → edit → **NH Restore Image Geometry** | [resolution](docs/en/resolution.md) |
| Inpaint only the clothes | Load Image → **Garment Segment** → **Mask Morphology** (grow) → inpaint | [vision](docs/en/vision.md) |
| Mask every person, window or the sky in a large photo | Load Image → **SAM3 Segment** → **Segs Select** or **Segs Compose** → mask → edit or inpaint | [vision](docs/en/vision.md#sam-3-workflows) |
| Compare many LoRAs, one per queue | **Counter** → **Load LoRA Model Index** (index) → sampler → **Image Label** (prints the LoRA name) → Save Image | [loaders](docs/en/loaders.md) |
| Run one of two branches without executing the other | condition → **If/Else** (lazy) → … | [logic](docs/en/logic.md#ifelse-nh) |
`Image Tile (NH)` supports:
## Node catalog
- `original_ratio`: derive tile dimensions from the original image aspect ratio.
- `custom`: use explicit tile width and height.
- `square`: force square tiles, for example `1024 x 1024`.
- Automatic overlap calculation.
- Edge padding so square/custom tile outputs keep the requested tile size.
| Group | Nodes | Guide |
|---|---|---|
| Image — load & save | Load Image Info, Load Images Folder, Load images matching, Save Image Path, NH Save Large Image | [EN](docs/en/image-io.md) · [VI](docs/vi/image-io.md) |
| Image — compose | Image Tile, Image Untile, Image Grid Composite, Layer Layout Composite, Layer Stack Composite, Image Compare, Image Label, Image Resize Unit | [EN](docs/en/image-compose.md) · [VI](docs/vi/image-compose.md) |
| Image — large preview | NH Large Image Preview, NH Large Image Compare, NH Large Preview Cache Manager | [EN](docs/en/image-preview.md) · [VI](docs/vi/image-preview.md) |
| Image — mask-based retouch | NH Mask-Aware Resize, NH Agnostic Image Generator, NH Simple Face Paste | [EN](docs/en/image-retouch.md) · [VI](docs/vi/image-retouch.md) |
| Resolution | NH Smart Resolution Picker, NH Smart Ratio Image Resize, NH Ratio Preset Image Resize, NH Remove Resize Padding, NH Restore Image Geometry | [EN](docs/en/resolution.md) · [VI](docs/vi/resolution.md) |
| Mask | Mask Morphology, Mask Properties, NH Create Box Mask, NH Mask Aspect Ratio Match | [EN](docs/en/mask.md) · [VI](docs/vi/mask.md) |
| Vision | Garment Segment, Portrait Segment, SAM3 Segment, Segs Select, Segs Compose, Segs To Masks, Segs To Impact SEGS, Segs Preview | [EN](docs/en/vision.md) · [VI](docs/vi/vision.md) |
| VTON | VTON Ultimate Processor | [EN](docs/en/vton.md) · [VI](docs/vi/vton.md) |
| Logic | Compare, Logic Gate, If/Else, Switch N, Any Switch, Any Branch Switch, Gate Switch, Value Match Index, Math Eval, Random Choice | [EN](docs/en/logic.md) · [VI](docs/vi/logic.md) |
| Text | Prompt Template, Prompt Scheduler, String Operations, Prompt Join, Text Split, Regex Extract, Text Index, Text Concatenate, Text Split Lines, Text Random Line | [EN](docs/en/text.md) · [VI](docs/vi/text.md) |
| Batch & lists | Batch Index, Batch Merge, Counter, List Create, List Index, List Filter, Any List Split | [EN](docs/en/batch-lists.md) · [VI](docs/vi/batch-lists.md) |
| Loaders | Load LoRA Model Index, Load LoRA Clip Index, Load Diffusion Model Index, Load VAE Index, Load Clip Index | [EN](docs/en/loaders.md) · [VI](docs/vi/loaders.md) |
| Utils | Multi-Slider (FLOAT/INT), Boolean Switch, Pack Universal, Unpack Universal, Universal Slider Builder *(deprecated)* | [EN](docs/en/utils.md) · [VI](docs/vi/utils.md) |
`Image Untile (NH)` uses `tile_data` from the tile node to stitch results back.
It also handles tiles that were resized or upscaled, such as 2x or 3x per tile.
All nodes live under **NH-Nodes** in the node menu; search by name or by node id (for example `NH_ImageTile`).
### Grid Contact Sheet
## Documentation
```text
Image batch -> Image Grid Composite (NH) -> Preview Image
```
Use manual cell size or first-image cell size. Resize modes include `stretch`,
`pad`, and `fill`.
### Metadata-Aware Image Load
```text
Load Image Info (NH) -> image / mask / file_name / path
```
Useful for naming outputs, logging source images, or routing by filename.
### Smart Resolution Resize
```text
Load Image -> NH Smart Ratio Image Resize -> (your pipeline) -> NH Remove Resize Padding
```
`NH Smart Ratio Image Resize` picks the closest model preset itself from
`model_family` + `resolution_level`, so it does not need `NH Smart Resolution
Picker` upstream. Resize with cover crop, contain pad, or stretch behavior.
In `contain_pad` mode it also emits `resize_data` describing the content region,
padding, and scale factor. Feed that into `NH Remove Resize Padding` at the end
of the pipeline to crop the padded border back off (`content_region`) or to
restore the original input size (`original_size`).
For `cover_crop`, use `NH Restore Image Geometry` after processing. Connect the
processed image to `image`, the source image to `original_image`, and the resize
node's `resize_data` to the matching input. It returns an image at the exact
original dimensions, with processed content placed back in its original region
and cropped-away borders retained from the source. Its `mask` is white in the
restored region and black in the retained borders; use it for masked blending
with the original, optionally softening the mask edges first.
Processed images may be upscaled, including by a non-integer factor, provided
their framing is unchanged. Additional crops, rotations, or model-generated
changes in geometry cannot be reversed by this metadata. The restore node also
accepts `contain_pad` and `stretch` metadata and supports matching batches or
singleton batch broadcasting. Restoration uses bicubic (default), bilinear, or
nearest sampling; it does not recover details lost during downsampling.
Use `NH Smart Resolution Picker` when you need target dimensions and a latent
for text-to-image, instead of resizing an existing image.
### Garment and Portrait Segmentation
```text
Load Image -> Garment Segment (NH) [part=upper_garment] -> mask / cutout_rgba / mask_image
Load Image -> Portrait Segment (NH) [hair + face] -> mask / cutout_rgba / mask_image
```
SegFormer-based masks at input resolution. `cutout_rgba` is the input image
with the mask as alpha (RGB is left untouched outside the mask); `mask_image`
is the mask as an RGB preview. Dresses and jumpsuits belong to `upper_garment`. Both nodes share `mask_expand`, `mask_blur`, and `fill_holes`.
Choose `part=all_garments` to combine upper/lower garments, footwear and headwear
in one mask, for example a shirt and pants together. `attach_parts` still controls
whether touching pockets, zippers and other garment decorations are included.
Models are auto-downloaded to `models/NH-Nodes/` on first use (existing
`models/face_parsing` or `models/RMBG/segformer_fashion` copies are reused).
---
## Node Catalog
### Image Loading and Saving
| Node | Purpose |
|---|---|
| `Load Image Info (NH)` | Load one image and output `IMAGE`, `MASK`, file name, and path. |
| `Save Image Path (NH)` | Save image batches to a chosen folder and output saved filenames/path/count. |
| `NH Save Large Image` | Save full-resolution images without attaching heavy full-res previews to the canvas. |
| `Load Images Folder (NH)` | Load images from a folder by index/step, with filename and path outputs. |
| `Load images matching` | Load source/matching images by text, folder context, and matching rules. |
### Image Tiling, Grid, and Composition
| Node | Purpose |
|---|---|
| `Image Tile (NH)` | Split image batches into overlapping tiles with metadata for reconstruction. |
| `Image Untile (NH)` | Rebuild full images from processed/upscaled tiles and tile metadata. |
| `Image Grid Composite (NH)` | Combine an image batch into a configurable grid. |
| `NH Large Image Preview` | Preview large images with lightweight canvas thumbnails and passthrough output. |
| `NH Large Image Compare` | Compare large A/B images with lightweight canvas proxy and tiled slider/side/difference viewer. |
| `NH Large Preview Cache Manager` | Inspect and clean the NH large preview tile cache by age, size, or cache id. |
| `Layer Layout Composite (NH)` | Place layers into preset, grid, or custom layout slots. |
| `Layer Stack Composite (NH)` | Paste multiple layers onto a background using explicit coordinates. |
| `Image Resize Unit (NH)` | Resize by pixel/mm/cm using stretch, pad, crop, or lock-ratio modes. |
| `Image Compare (NH)` | Compare two images side-by-side, top/bottom, split, difference, or overlay. |
| `Image Label (NH)` | Add header/bottom labels with font, padding, color, and outline controls. |
| `NH Agnostic Image Generator` | Create agnostic/masked/composite images for inpaint and VTON workflows. |
| `NH Simple Face Paste` | Paste a source face region into a target using mask feathering. |
### Smart Resolution
| Node | Purpose |
|---|---|
| `NH Smart Resolution Picker` | Pick model/preset-aware dimensions and latent setup. |
| `NH Smart Ratio Image Resize` | Auto-pick the closest model preset for the source aspect, then resize with cover, contain, or stretch; outputs `resize_data` for padding removal. |
| `NH Ratio Preset Image Resize` | Resize to a chosen model preset ratio with crop, pad, or fill behavior. |
| `NH Remove Resize Padding` | Remove the padded border from processed images using resize metadata. |
| `NH Restore Image Geometry` | Restore processed or upscaled images to original coordinates with a blend mask; keep source pixels outside the crop. |
### Vision Segmentation
| Node | Purpose |
|---|---|
| `Garment Segment (NH)` | Mask `upper_garment` (tops, jackets, coats, dresses, ties, scarves, collars, sleeves), `lower_garment` (pants, shorts, skirts, belts, tights; no shoes), `footwear`, `headwear`, or combine all four with `all_garments`. Attached parts (pockets, zippers, bows...) merge only when touching the selected garments. |
| `Portrait Segment (NH)` | Checklist mask of face, neck, hair, eyes, lips, nose, eyebrows, ears (+ optional mouth interior). `face` is the solid face region including features. |
### Mask Tools
| Node | Purpose |
|---|---|
| `Mask Morphology (NH)` | Expand/shrink masks, fill holes, and blur edges. |
| `Mask Properties (NH)` | Output mask bounding box, dimensions, and related measurements. |
| `NH Create Box Mask` | Convert a mask area into a rectangular box mask. |
| `NH Mask Aspect Ratio Match` | Adjust one mask to match another mask's aspect ratio. |
| `NH Mask-Aware Resize` | Resize an image/mask pair while preserving the masked subject region. |
### Logic and Flow Control
| Node | Purpose |
|---|---|
| `Compare (NH)` | Compare values with typed operators. |
| `Logic Gate (NH)` | AND, OR, NOT, XOR, NAND, and NOR boolean logic. |
| `If/Else (NH)` | Select between two inputs by condition. |
| `Switch N (NH)` | Pick one of several generic inputs by index. |
| `Any Switch (NH)` | Boolean switch helper. |
| `Any Branch Switch (NH)` | Route generic data through selected branches. |
| `Gate Switch (NH)` | Pass or block a generic input by boolean state. |
| `Value Match Index (NH)` | Return index of matching configured value. |
| `Math Eval (NH)` | Safe math expression evaluator with numeric/string outputs. |
| `Random Choice (NH)` | Weighted random choice from generic inputs. |
### Text and Prompt Tools
| Node | Purpose |
|---|---|
| `String Operations (NH)` | Strip, case convert, replace, slice, inspect, and test strings. |
| `Prompt Join (NH)` | Join text fragments with separators and empty-line handling. |
| `Text Split (NH)` | Split text by delimiter and expose count/first/last values. |
| `Regex Extract (NH)` | Extract, replace, split, and match with regex patterns. |
| `Text Index (NH)` | Select a line/item from text by index. |
| `Text Concatenate (NH)` | Concatenate multiple text inputs. |
| `Text Split Lines (NH)` | Split multiline text into selected parts and metadata. |
| `Text Random Line (NH)` | Pick a random line from multiline text. |
| `Prompt Template (NH)` | Fill `{placeholder}` variables from connected text values. |
| `Prompt Scheduler (NH)` | Select prompts by step, sequence, ping-pong, or seed. |
### Lists and Batch Helpers
| Node | Purpose |
|---|---|
| `List Create (NH)` | Build an `NH_LIST` from multiline or delimited text. |
| `List Index (NH)` | Select an item from an `NH_LIST`. |
| `List Filter (NH)` | Filter lists with contains, regex, equals, length, and related conditions. |
| `Batch Index (NH)` | Extract one or more images from an image batch. |
| `Batch Merge (NH)` | Merge image batches with resize handling. |
| `Counter (NH)` | Stateful counter for repeated queue runs. |
| `Any List Split (NH)` | Split any incoming list into an `All` output plus up to 10 indexed outputs. |
### Indexed Loaders
| Node | Purpose |
|---|---|
| `Load LoRA Model Index (NH)` | Apply one selected LoRA to `MODEL` by 1-based index. |
| `Load LoRA Clip Index (NH)` | Apply one selected LoRA to `CLIP` by 1-based index. |
| `Load Diffusion Model Index (NH)` | Load one diffusion model by 1-based index. |
| `Load VAE Index (NH)` | Load one VAE by 1-based index or boolean toggle. |
| `Load Clip Index (NH)` | Load one CLIP by 1-based index or boolean toggle, with CLIP type selection. |
### Workflow Utilities
| Node | Purpose |
|---|---|
| `Multi-Slider (FLOAT)` | Five float controls in one node. |
| `Multi-Slider (INT)` | Five integer controls in one node. |
| `Universal Slider Builder (NH)` | Generate dynamic slider outputs from text config. |
| `Boolean Switch (NH)` | Simple boolean toggle. |
| `Pack Universal (NH)` | Bundle generic values into an `NH_UNIVERSAL_PIPE`. |
| `Unpack Universal (NH)` | Extract values from an `NH_UNIVERSAL_PIPE`. |
### VTON Preprocessing
| Node | Purpose |
|---|---|
| `VTON Ultimate Processor (NH)` | Generate masks, agnostic image data, DensePose/parsing outputs, hair/hands masks, and related VTON preprocessing assets. |
---
## Menu Structure
```text
NH-Nodes/
Image/
Load/save helpers
Tile/untile
Grid and layer composition
Resize, compare, label
Mask/
Morphology, properties, bbox, aspect matching
Vision/
Garment and portrait segmentation
Logic/
Compare, gates, switches, branching, math, random choice
Text/
String, regex, split, concatenate, templates, scheduling
Batch/
Image batch, list helpers, any-list split
Resolution/
Smart resolution picker, ratio resize, preset resize, padding removal
Loaders/
Indexed LoRA, diffusion model, VAE, and CLIP loaders
Utils/
Sliders, booleans, universal pipe
VTON/
VTON Ultimate Processor
```
---
## Requirements
```text
numpy
Pillow
scipy
opencv-python
scikit-image>=0.18.0
transformers>=4.20.0
tqdm>=4.62.0
huggingface_hub>=0.16.0
```
`VTON Ultimate Processor (NH)` also needs ONNX Runtime, which is not installed
automatically (the CPU wheel would overwrite an existing GPU build). Install one:
`python -m pip install onnxruntime-gpu` (NVIDIA) or `python -m pip install onnxruntime` (CPU).
Most image/text/logic nodes use common ComfyUI dependencies. VTON and Vision
nodes download model assets on first use (`transformers` SegFormer weights for
Vision: ~190 MB fashion, ~340 MB face).
---
## Development Notes
- Layout: `core/` holds shared helpers and model backends (no nodes); `nodes/<category>/`
mirrors the ComfyUI menu, and every module there that defines `NODE_CLASS_MAPPINGS`
is registered automatically by `__init__.py` - drop a file in the right folder
and restart. VTON's vendored preprocessing lives in `nodes/vton/preprocess/`.
- Tests: `python -m pytest custom_nodes/NH-Nodes/tests -q` from the ComfyUI root.
- Restart ComfyUI after installing or updating nodes.
- If a node appears cached in the browser, refresh the ComfyUI page after restart.
- `Image Tile (NH)` and `Image Untile (NH)` must pass `tile_data` directly through
the workflow so reconstruction uses the original tile layout.
---
- **Guide:** [English](docs/en/README.md) · [Tiếng Việt](docs/vi/README.md)
- **PDF guides:** [English](https://github.com/jetthuangai/NH-Nodes/releases/latest/download/NH-Nodes-Guide-EN.pdf) ·
[Tiếng Việt](https://github.com/jetthuangai/NH-Nodes/releases/latest/download/NH-Nodes-Huong-dan-VI.pdf)
- **Changelog:** [docs/CHANGELOG.md](docs/CHANGELOG.md) — read it when updating: some fixes change outputs on purpose.
## License
NH-Nodes' own code is [MIT](LICENSE). `VTON Ultimate Processor (NH)` and
`Portrait Segment (NH)` depend on third-party code or models that are licensed for
**non-commercial use only**, and the model behind `Garment Segment (NH)` declares no
license. See [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md) before using these
three nodes commercially.
NH-Nodes' own code is [MIT](LICENSE). Some features rely on third-party code or models with other terms — see
[THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md):
If NH-Nodes helps your workflow, consider starring the repository:
[github.com/jetthuangai/NH-Nodes](https://github.com/jetthuangai/NH-Nodes)
- **VTON Ultimate Processor** and **Portrait Segment** are for **non-commercial use only** (CC BY-NC-SA 4.0 code and
parsing models; non-commercial face-parsing model).
- **Garment Segment**'s model does not declare a licence.
- **SAM3 Segment** uses Meta's SAM 3 under the SAM License: royalty-free, with no non-commercial clause, but it bars
military or warfare, nuclear, espionage, and gun or illegal-weapon uses. The weights and the `sam3` package are not
included; you get them from Meta.
## Development
- Layout: `core/` holds shared helpers and model backends (no nodes); `nodes/<category>/` mirrors the menu, and every
module there that defines `NODE_CLASS_MAPPINGS` is registered automatically; `web/` holds the frontend extensions.
- Tests and lint (run from the ComfyUI root / the pack folder):
```bash
python -m pytest custom_nodes/NH-Nodes/tests -q
ruff check .
```
- Docs assets are generated from the live code: `docs/_tools/build_node_cards.py` (node cards) and
`docs/_tools/build_examples.py` (example images), both run from the ComfyUI root.
## Author
[jetthuang.com](https://jetthuang.com) · [Hugging Face](https://huggingface.co/nhathoangfoto) ·
[Support via PayPal](https://paypal.me/nhathoangfoto). If NH-Nodes helps your work, a ⭐ on GitHub is appreciated.
+32 -1
View File
@@ -16,6 +16,7 @@ MIT license.
| `VTON Ultimate Processor (NH)` | **No** | Its code and parsing models are CC BY-NC-SA 4.0 (FitDiT / OOTDiffusion) |
| `Portrait Segment (NH)` | **No** | The face-parsing model card restricts use to non-commercial research and education |
| `Garment Segment (NH)` | Unclear | The model uploader does not declare a license |
| `SAM3 Segment (NH)` | Yes, within trade-control limits | Meta's SAM License is royalty-free and has no non-commercial clause, but it forbids ITAR-regulated activities and uses that trade controls prohibit (military or warfare, nuclear, espionage, guns and illegal weapons). The model and the `sam3` package are not part of NH-Nodes: you get them from Meta |
## Source code bundled in this repository
@@ -55,8 +56,38 @@ They are looked up first in the folders registered as `vton_mask` and `nh_nodes`
| Portrait Segment | [`jonathandinu/face-parsing`](https://huggingface.co/jonathandinu/face-parsing) @ `758b82e` | SegFormer fine-tuned on CelebAMask-HQ | Model card: "non-commercial research and educational purposes" |
| Garment Segment | [`1038lab/segformer_fashion`](https://huggingface.co/1038lab/segformer_fashion) @ `e5bae2d` | SegFormer trained for Fashionpedia classes | No license declared by the uploader (Fashionpedia annotations are CC BY 4.0) |
## Software and models you install yourself
`SAM3 Segment (NH)` runs Meta's SAM 3. NH-Nodes neither bundles nor downloads it, and the
nodes only load it when you use them. You get both parts from Meta and accept Meta's terms
yourself.
| What | Where it comes from | Terms |
|------|---------------------|-------|
| `sam3` package (code) | [`facebookresearch/sam3`](https://github.com/facebookresearch/sam3), installed by the user with pip | SAM License |
| SAM 3 checkpoint `sam3.pt` | [`facebook/sam3`](https://huggingface.co/facebook/sam3) on Hugging Face, a gated repository: access is granted after the user accepts the license. It goes in the `sam3` model folder (`models/sam3`, or any location added under `sam3` in `extra_model_paths.yaml`), which other SAM 3 packs share | SAM License |
The [SAM License](https://github.com/facebookresearch/sam3/blob/main/LICENSE) (Meta, last
updated 19 November 2025) is a non-exclusive, worldwide, non-transferable, royalty-free
limited license to use, reproduce, distribute, copy, modify and create derivative works of
the SAM Materials. It does not mention commercial use. What matters to a user of NH-Nodes:
- **Trade controls.** You must not be a target of trade controls and must comply with them.
The materials may not be used for activities subject to the International Traffic in Arms
Regulations (ITAR), or for end uses that trade controls prohibit, including military or
warfare purposes, nuclear industries or applications, espionage, and the development or
use of guns or illegal weapons.
- Whoever redistributes the SAM Materials must include a copy of the license. NH-Nodes does
not redistribute them.
- Research publications that use the SAM Materials must acknowledge it.
- The materials may not be used to reverse engineer or decompile them, nor to encourage
others to.
- They are provided "as is", and Meta may terminate the license if you breach it.
## Python dependencies
Dependencies listed in `requirements.txt` are installed from PyPI and are not
redistributed here; each keeps its own license. ONNX Runtime (MIT) is optional
and only needed by the VTON node.
and only needed by the VTON node. The `sam3` package (SAM License) and its extras
(`timm`, `ftfy`, `regex`, `iopath`) are optional too: only `SAM3 Segment (NH)` needs
them, and you install them yourself.
+626
View File
@@ -0,0 +1,626 @@
"""SAM 3 for the NH vision nodes: finding the model, loading it, and segmenting concepts in two passes.
NH-Nodes does not ship SAM 3. It uses Meta's ``sam3`` package, installed separately (see ``INSTALL_HINT``), and the
``sam3.pt`` checkpoint, which Hugging Face hands out only after Meta's license is accepted, so it is never
downloaded automatically. Checkpoints are looked up in the ``sam3`` model folder (``models/sam3`` by default, more
locations in ``extra_model_paths.yaml``); other SAM 3 packs use the same folder, so one copy serves all of them.
Pass 1 looks at the whole image once: the image is resized to the model's square input, encoded once and asked for
every concept. Each instance's mask is sampled back to full resolution inside its own box only.
Pass 2 (``detail_level="balanced"``) cuts each instance out of the full-resolution image with a margin and segments it
again with SAM 3's box-prompt decoder, the pass-1 mask given as a hint. Objects that are small or medium in the frame
then get their edges from the original pixels instead of a ~1000 px view of the whole frame. SAM 3 draws masks on a
grid of about 288 cells across whatever it looks at, so a subject that fills most of an 8K frame still has edges at
about 1/300 of its size; hair-level edges need matting. A close-up that does not show the pass-1 instance is dropped
and the pass-1 mask kept.
The official sam3 code builds some of its lookup tables on the GPU, so SAM 3 runs on CUDA only.
"""
import contextlib
import importlib
import importlib.util
import inspect
import itertools
import math
import os
import cv2
import numpy as np
import torch
import torch.nn.functional as F
from .model_files import make_room_on_gpu, models_dir, register_model_folder
from .segs import SegsFrame, make_segment
try:
import folder_paths
except Exception: # running outside ComfyUI (tests)
folder_paths = None
try:
import comfy.model_management as _mm
except Exception:
_mm = None
FOLDER_NAME = "sam3" # shared with the other SAM 3 packs
# Listed in ComfyUI for the folder: SAM 3 checkpoints in the formats SAM 3 packs use, not Hugging Face cache files.
FOLDER_EXTENSIONS = (".pt", ".pth", ".safetensors")
CHECKPOINT_EXTENSIONS = (".pt", ".pth") # what sam3's own loader reads (torch.load)
DEFAULT_CHECKPOINT = "sam3.pt"
DETAIL_LEVELS = ("balanced", "fast")
SAM3_COMMIT = "b26a5f330e05d321afb39d01d3d4881f258f65ff" # the sam3 build NH-Nodes is tested with
INSTALL_HINT = (
"SAM 3 needs Meta's sam3 package, which NH-Nodes does not install for you. In ComfyUI's Python environment run:\n"
f" pip install --no-deps git+https://github.com/facebookresearch/sam3@{SAM3_COMMIT}\n"
" pip install timm ftfy regex iopath\n"
"--no-deps keeps your numpy: sam3 asks for numpy<2, which ComfyUI does not need. "
"Restart ComfyUI afterwards."
)
CHECKPOINT_HINT = (
"Ask for access at https://huggingface.co/facebook/sam3 (Meta has you accept the SAM License first), "
"download sam3.pt from that page and put it in {folder}. Then refresh ComfyUI (press R) and pick it."
)
DEDUP_IOU = 0.9 # a second instance of the same concept overlapping a better one this much is a duplicate
# A close-up replaces the pass-1 mask when it shows the same object: at least REFINE_MIN_INSIDE of it lies within the
# pass-1 blur of the pass-1 mask, and it covers at least REFINE_MIN_CORE of the pass-1 core (see close_up_agrees).
REFINE_MIN_INSIDE = 0.8
REFINE_MIN_CORE = 0.6
_PASS1_GRID = 288 # SAM 3's detector draws masks on a 288 x 288 grid of its square view of the whole image
_AGREE_PIXELS = 1 << 21 # masks larger than this are compared at a reduced scale
_WIDE_ASPECT = 4.0 # beyond this aspect ratio SAM 3's square view squeezes the picture enough to miss small things
_CROP_MARGIN = 0.2 # pass-2 crop margin on each side, as a fraction of the instance's longer side
_CROP_MIN_MARGIN = 16 # px
_CROP_MAX_ASPECT = 2.0 # pass-2 crops are widened to at most 2:1, the model squeezes them into a square
_SAMPLE_CHUNK = 1 << 22 # output pixels per grid_sample call when resampling masks
_WORKING_BYTES = 3 << 30 # VRAM for activations and the full-resolution image, on top of the weights
_API = None
_CACHE = {}
# ---------------------------------------------------------------------------
# Checkpoints
# ---------------------------------------------------------------------------
def default_folder():
"""<models dir>/sam3, the folder ComfyUI and the other SAM 3 packs use."""
return os.path.join(models_dir(), FOLDER_NAME)
def checkpoint_folders():
"""Every folder searched for checkpoints, best first (registers the folder name with ComfyUI)."""
return register_model_folder(FOLDER_NAME, default_folder(), FOLDER_EXTENSIONS)
def checkpoint_choices():
"""Checkpoint names for the node's list: .pt/.pth files in the sam3 folders, or the default name if none yet."""
names = set()
folders = checkpoint_folders()
if folder_paths is not None:
try:
names.update(folder_paths.get_filename_list(FOLDER_NAME))
except Exception as exc:
print(f"[NH-Nodes] Could not list the {FOLDER_NAME} model folder: {exc}")
else:
for folder in folders:
if os.path.isdir(folder):
names.update(os.listdir(folder))
names = sorted(name for name in names if name.lower().endswith(CHECKPOINT_EXTENSIONS))
return names or [DEFAULT_CHECKPOINT]
def resolve_checkpoint(name):
"""Full path of checkpoint ``name`` in the sam3 folders; a RuntimeError says where to get it when it is missing."""
name = str(name or DEFAULT_CHECKPOINT)
parts = name.replace("\\", "/").split("/")
if os.path.isabs(name) or ".." in parts:
raise RuntimeError(f"[NH-Nodes] SAM 3 checkpoint name must be a file in the {FOLDER_NAME} folder: {name!r}")
if not name.lower().endswith(CHECKPOINT_EXTENSIONS):
raise RuntimeError(
f"[NH-Nodes] {name} is not a SAM 3 .pt checkpoint. The safetensors export on Hugging Face is for the "
"transformers library and uses other weight names; download sam3.pt instead."
)
folders = checkpoint_folders()
path = folder_paths.get_full_path(FOLDER_NAME, name) if folder_paths is not None else None
if path is None:
path = next((os.path.join(folder, *parts) for folder in folders
if os.path.isfile(os.path.join(folder, *parts))), None)
if path is None or not os.path.isfile(path):
raise RuntimeError(
f"[NH-Nodes] SAM 3 checkpoint {name} not found in: {', '.join(folders)}.\n"
+ CHECKPOINT_HINT.format(folder=folders[0] if folders else default_folder())
)
return path
# Make the folder name known to ComfyUI as soon as the pack loads.
checkpoint_folders()
# ---------------------------------------------------------------------------
# The sam3 package and the model
# ---------------------------------------------------------------------------
def _tf32_flags():
"""(matmul, cudnn) TF32 switches, or None when torch refuses to report them (its newer precision API in use)."""
try:
return torch.backends.cuda.matmul.allow_tf32, torch.backends.cudnn.allow_tf32
except Exception:
return None
def _restore_tf32(flags):
if flags is not None:
try:
torch.backends.cuda.matmul.allow_tf32, torch.backends.cudnn.allow_tf32 = flags
except Exception:
pass
def _missing_parameters(func, names):
"""Names from ``names`` that ``func`` does not accept (none when its signature cannot be read or takes **kwargs)."""
try:
parameters = inspect.signature(func).parameters
except (TypeError, ValueError):
return []
if any(p.kind == p.VAR_KEYWORD for p in parameters.values()):
return []
return [name for name in names if name not in parameters]
def ensure_sam3():
"""(build_sam3_image_model, Sam3Processor) from the installed sam3 package.
A RuntimeError explains what to do when the package is missing, fails to import or lacks the image API
NH-Nodes uses. Importing sam3's model builder switches TF32 on for the whole process; the previous setting is
put back, so ComfyUI's own models keep theirs.
"""
global _API
if _API is not None:
return _API
if importlib.util.find_spec("sam3") is None:
raise RuntimeError("[NH-Nodes] " + INSTALL_HINT)
flags = _tf32_flags()
try:
builder = importlib.import_module("sam3.model_builder")
processors = importlib.import_module("sam3.model.sam3_image_processor")
except Exception as exc:
raise RuntimeError(f"[NH-Nodes] The sam3 package is installed but failed to load: {exc}\n{INSTALL_HINT}") from exc
finally:
_restore_tf32(flags)
build = getattr(builder, "build_sam3_image_model", None)
processor_cls = getattr(processors, "Sam3Processor", None)
missing = []
if not callable(build):
missing.append("build_sam3_image_model")
else:
missing += [f"build_sam3_image_model({name}=)" for name in _missing_parameters(
build, ("checkpoint_path", "load_from_HF", "enable_inst_interactivity", "device", "eval_mode"))]
if processor_cls is None:
missing.append("Sam3Processor")
else:
missing += [f"Sam3Processor({name}=)" for name in
_missing_parameters(processor_cls, ("device", "confidence_threshold"))]
missing += [f"Sam3Processor.{name}" for name in ("set_image", "set_text_prompt", "reset_all_prompts")
if not callable(getattr(processor_cls, name, None))]
if missing:
raise RuntimeError(
f"[NH-Nodes] The installed sam3 package lacks {', '.join(missing)}; it is older or newer than the "
f"versions NH-Nodes supports. Reinstall it:\n{INSTALL_HINT}"
)
_API = (build, processor_cls)
return _API
def sam3_device():
"""ComfyUI's torch device, which must be a CUDA GPU for SAM 3."""
if _mm is not None:
device = _mm.get_torch_device()
else:
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
if device.type != "cuda":
raise RuntimeError(
f"[NH-Nodes] SAM 3 needs an NVIDIA GPU with CUDA (ComfyUI runs on {device}): the official sam3 code "
"builds part of the model on the GPU."
)
return device
def check_checkpoint(path):
"""Fail early, with the download advice, when ``path`` is not a readable SAM 3 checkpoint.
sam3 loads weights non-strictly and only prints the names it misses, so another model's .pt in the shared folder
would run with random weights, and a truncated download fails deep inside torch. Reading the names is quick:
the file is memory-mapped, not loaded.
"""
advice = CHECKPOINT_HINT.format(folder=os.path.dirname(path))
try:
state = torch.load(path, map_location="cpu", weights_only=True, mmap=True)
except Exception as exc:
raise RuntimeError(f"[NH-Nodes] {path} cannot be read as a SAM 3 checkpoint ({exc}); it may be an "
f"incomplete download.\n{advice}") from exc
if isinstance(state, dict) and isinstance(state.get("model"), dict):
state = state["model"]
names = list(state.keys()) if isinstance(state, dict) else []
del state
if not any(name.startswith("detector.") for name in names) or not any(name.startswith("tracker.") for name in names):
raise RuntimeError(f"[NH-Nodes] {path} is not a SAM 3 checkpoint (it has no SAM 3 detector and tracker "
f"weights).\n{advice}")
def load_model(checkpoint):
"""The SAM 3 image model for ``checkpoint`` (a name in the sam3 folders); kept in RAM between runs."""
path = resolve_checkpoint(checkpoint)
stamp = (os.path.normcase(os.path.abspath(path)), os.path.getmtime(path), os.path.getsize(path))
if _CACHE.get("model") is not None and _CACHE.get("stamp") == stamp:
return _CACHE["model"]
unload()
build, _ = ensure_sam3()
device = sam3_device()
check_checkpoint(path)
print(f"[NH-Nodes] Loading SAM 3 from {path}")
with torch.cuda.device(device): # the tables sam3 builds on "cuda" go to ComfyUI's GPU
model = build(checkpoint_path=path, load_from_HF=False, enable_inst_interactivity=True,
device="cpu", eval_mode=True)
if not callable(getattr(model, "predict_inst", None)) or getattr(model, "inst_interactive_predictor", None) is None:
raise RuntimeError("[NH-Nodes] This sam3 build has no box-prompt decoder (predict_inst). " + INSTALL_HINT)
transforms = getattr(model.inst_interactive_predictor, "_transforms", None)
if getattr(transforms, "max_hole_area", 0):
# sam3 fills holes of close-up masks by default; keep real ones (between an arm and the body), as pass 1 does
transforms.max_hole_area = 0.0
model.requires_grad_(False)
_CACHE.update(model=model, stamp=stamp)
return model
def _model_device(model):
return next(model.parameters()).device
def _is_on(current, wanted):
"""True when ``current`` satisfies ``wanted``; an index-less "cuda" accepts any cuda:N."""
return current.type == wanted.type and (wanted.index is None or current.index == wanted.index)
_TABLE_ATTRIBUTES = ("freqs_cis", "freqs_cis_real", "freqs_cis_imag") # sam3's rotary tables, plain attributes
def _all_on(model, device):
return all(_is_on(p.device, device) for p in model.parameters())
def _move_tables(model, device):
"""Move sam3's rotary tables, which model.to() leaves behind because they are not buffers."""
for module in model.modules():
for name in _TABLE_ATTRIBUTES:
value = getattr(module, name, None)
if isinstance(value, torch.Tensor) and not _is_on(value.device, device):
setattr(module, name, value.to(device))
def move_model(model, device):
"""Put the weights on ``device``, asking ComfyUI to free VRAM first; back to the CPU if the move fails."""
if device.type == "cuda":
weights = 0 if _all_on(model, device) else sum(
t.numel() * t.element_size() for t in itertools.chain(model.parameters(), model.buffers()))
make_room_on_gpu(weights + _WORKING_BYTES)
try:
model.to(device)
_move_tables(model, device)
except Exception:
offload(model) # a half-moved model would fail every later run
raise
def offload(model):
"""Move the weights and sam3's GPU-side tables to the CPU, so nothing holds VRAM between runs.
The position-encoding caches sam3 fills at build time (about 200 MB on the GPU) are emptied; sam3 rebuilds them
on the GPU when it needs them again.
"""
cpu = torch.device("cpu")
if not _all_on(model, cpu):
model.to(cpu)
_move_tables(model, cpu)
for module in model.modules():
if type(module).__name__ == "PositionEmbeddingSine" and isinstance(getattr(module, "cache", None), dict):
module.cache.clear()
if torch.cuda.is_available():
torch.cuda.empty_cache()
def unload():
"""Forget the cached model (another checkpoint was chosen)."""
model = _CACHE.pop("model", None)
_CACHE.pop("stamp", None)
if model is not None:
offload(model)
def _native_bf16():
try:
return torch.cuda.is_bf16_supported(including_emulation=False)
except TypeError: # torch without the including_emulation switch
return torch.cuda.is_bf16_supported()
def _autocast(device):
"""bfloat16 autocast on GPUs with native bfloat16 (Ampere and newer), as Meta runs SAM 3; full precision elsewhere."""
if device.type == "cuda" and _native_bf16():
return torch.autocast("cuda", dtype=torch.bfloat16)
return contextlib.nullcontext()
# ---------------------------------------------------------------------------
# Geometry helpers (model-free, unit tested)
# ---------------------------------------------------------------------------
def to_logits(masks):
"""sam3 returns its masks after a sigmoid (under the key "masks_logits"); turn them back into logits.
Values outside [0, 1] are taken as logits already, in case a later sam3 returns them as the key says.
"""
masks = masks.float()
if masks.numel() and (float(masks.min()) < 0.0 or float(masks.max()) > 1.0):
return masks
masks = masks.clamp(1e-6, 1.0 - 1e-6)
return torch.log(masks) - torch.log1p(-masks)
def sample_region(logits, image_size, region, out_size):
"""Resample the square model-space map ``logits`` [S, S] over ``region`` of the image, bilinearly.
``region`` is (x1, y1, x2, y2) in pixels of an image of ``image_size`` (height, width), which the model saw
squeezed into S x S; the result is [h, w] for ``out_size`` (h, w), each value taken at the centre of its output
pixel. Runs in row chunks, so a full 8K region needs a few hundred MB at most.
"""
height, width = image_size
size = logits.shape[-1]
x1, y1, x2, y2 = region
out_h, out_w = int(out_size[0]), int(out_size[1])
device = logits.device
xs = x1 + (torch.arange(out_w, device=device, dtype=torch.float32) + 0.5) * ((x2 - x1) / out_w)
ys = y1 + (torch.arange(out_h, device=device, dtype=torch.float32) + 0.5) * ((y2 - y1) / out_h)
# model-map pixel index of each centre, as grid_sample coordinates (align_corners=True: -1/+1 = first/last centre)
grid_x = (xs * (size / width) - 0.5) * (2.0 / max(1, size - 1)) - 1.0
grid_y = (ys * (size / height) - 0.5) * (2.0 / max(1, size - 1)) - 1.0
source = logits.float()[None, None]
result = torch.empty((out_h, out_w), device=device, dtype=torch.float32)
rows = max(1, _SAMPLE_CHUNK // max(1, out_w))
for start in range(0, out_h, rows):
gy = grid_y[start:start + rows]
grid = torch.stack(torch.broadcast_tensors(grid_x[None, :], gy[:, None]), dim=-1)[None]
result[start:start + rows] = F.grid_sample(source, grid, mode="bilinear", padding_mode="border",
align_corners=True)[0, 0]
return result
def mask_box(mask):
"""Tight (x1, y1, x2, y2) box of a bool [H, W] tensor with exclusive ends; None when empty."""
rows = torch.nonzero(mask.any(dim=1)).flatten()
if rows.numel() == 0:
return None
cols = torch.nonzero(mask.any(dim=0)).flatten()
return int(cols[0]), int(rows[0]), int(cols[-1]) + 1, int(rows[-1]) + 1
def model_box_to_image(box, size, image_size, margin=1):
"""An S x S model-space box as an image-space region, ``margin`` model pixels wider on each side, in the image."""
height, width = image_size
sx, sy = width / size, height / size
x1, y1, x2, y2 = box
return (max(0, int((x1 - margin) * sx)), max(0, int((y1 - margin) * sy)),
min(width, int(math.ceil((x2 + margin) * sx))), min(height, int(math.ceil((y2 + margin) * sy))))
def _fit_span(center, size, limit):
size = int(round(min(size, limit)))
start = int(round(center - size / 2.0))
start = min(max(0, start), limit - size)
return start, start + size
def crop_region(box, image_size):
"""Pass-2 crop around an image-space box: a margin on each side, widened to at most 2:1, kept inside the image."""
height, width = image_size
x1, y1, x2, y2 = box
margin = max(_CROP_MIN_MARGIN, _CROP_MARGIN * max(x2 - x1, y2 - y1))
crop_w, crop_h = (x2 - x1) + 2 * margin, (y2 - y1) + 2 * margin
crop_w, crop_h = max(crop_w, crop_h / _CROP_MAX_ASPECT), max(crop_h, crop_w / _CROP_MAX_ASPECT)
left, right = _fit_span((x1 + x2) / 2.0, crop_w, width)
top, bottom = _fit_span((y1 + y2) / 2.0, crop_h, height)
# the box itself always stays inside, whatever the rounding did
return min(left, x1), min(top, y1), max(right, x2), max(bottom, y2)
def drop_stray_parts(refined, coarse):
"""``refined`` without the connected parts that do not touch ``coarse`` at all (specks around the object)."""
count, labels = cv2.connectedComponents(refined.astype(np.uint8), connectivity=8)
if count <= 2:
return refined
touching = np.bincount(labels[coarse], minlength=count) > 0
touching[0] = False
return touching[labels]
def close_up_agrees(refined, coarse, tolerance):
"""True when the close-up mask ``refined`` shows the same object as the pass-1 mask ``coarse`` (bool arrays).
The pass-1 mask is blurred by up to ``tolerance`` px, so plain IoU would turn down a sharp mask of a thin pole or
cable. Instead most of the close-up must lie within ``tolerance`` of the pass-1 mask, and it must cover most of
the pass-1 core (pixels deeper than twice ``tolerance`` inside it); thin objects have no core and need only the
first. Large masks are compared at a reduced scale.
"""
if not refined.any() or not coarse.any():
return False
step = max(1, int(math.sqrt(refined.size / _AGREE_PIXELS)))
if step > 1 and not refined[::step, ::step].any():
step = 1 # something thinner than the step: compare at full scale
refined, coarse = refined[::step, ::step], coarse[::step, ::step]
tolerance = max(1.0, tolerance / step)
to_coarse = cv2.distanceTransform((~coarse).astype(np.uint8), cv2.DIST_L2, 3)
inside = np.count_nonzero(refined & (to_coarse <= tolerance)) / np.count_nonzero(refined)
core = cv2.distanceTransform(coarse.astype(np.uint8), cv2.DIST_L2, 3) > 2.0 * tolerance
covered = np.count_nonzero(refined & core) / np.count_nonzero(core) if core.any() else 1.0
return inside >= REFINE_MIN_INSIDE and covered >= REFINE_MIN_CORE
def _box_overlap(a, b):
"""Intersection box of two (x1, y1, x2, y2) boxes, or None."""
x1, y1, x2, y2 = max(a[0], b[0]), max(a[1], b[1]), min(a[2], b[2]), min(a[3], b[3])
return (x1, y1, x2, y2) if x1 < x2 and y1 < y2 else None
def find_instances(processor, state, concept, max_instances=0):
"""[(score, logits [S, S], model-space box)] for one concept on an encoded image, best first, duplicates dropped."""
processor.reset_all_prompts(state)
output = processor.set_text_prompt(concept, state)
masks, scores = output["masks_logits"], output["scores"]
if masks is None or len(scores) == 0:
return []
scores = scores.float().flatten()
found, kept = [], [] # kept: (bool mask, box, area) of accepted instances
for i in torch.argsort(scores, descending=True).tolist():
logits = to_logits(masks[i].reshape(masks.shape[-2], masks.shape[-1]))
inside = logits > 0
box = mask_box(inside)
if box is None:
continue
area = int(inside.sum())
duplicate = False
for other, other_box, other_area in kept:
overlap = _box_overlap(box, other_box)
if overlap is None:
continue
ox1, oy1, ox2, oy2 = overlap
inter = int((inside[oy1:oy2, ox1:ox2] & other[oy1:oy2, ox1:ox2]).sum())
if inter / max(1, area + other_area - inter) >= DEDUP_IOU:
duplicate = True
break
if duplicate:
continue
kept.append((inside, box, area))
found.append((float(scores[i]), logits, box))
if max_instances and len(found) >= max_instances:
break
return found
# ---------------------------------------------------------------------------
# Segmenting
# ---------------------------------------------------------------------------
def _hint_size(model):
"""Side of the low-resolution mask the box-prompt decoder takes as a hint (288 for SAM 3)."""
encoder = getattr(getattr(getattr(model, "inst_interactive_predictor", None), "model", None),
"sam_prompt_encoder", None)
size = getattr(encoder, "mask_input_size", (288, 288))
return int(size[0]), int(size[1])
def _upsample(concept, score, logits, box, image_size):
"""Pass 1 only: the instance's mask at full resolution, inside its own region."""
region = model_box_to_image(box, logits.shape[-1], image_size)
x1, y1, x2, y2 = region
mask = sample_region(logits, image_size, region, (y2 - y1, x2 - x1)) > 0
return make_segment(concept, score, mask, origin=(x1, y1)), False
def _refine(model, processor, image, concept, score, logits, box, image_size, hint_size):
"""Pass 2: segment the instance again on a full-resolution crop, the pass-1 mask as a hint.
Returns (segment, refined) where ``refined`` is False when the pass-1 mask was kept.
"""
target = model_box_to_image(box, logits.shape[-1], image_size)
cx1, cy1, cx2, cy2 = crop_region(target, image_size)
coarse = (sample_region(logits, image_size, (cx1, cy1, cx2, cy2), (cy2 - cy1, cx2 - cx1)) > 0).cpu().numpy()
hint = sample_region(logits, image_size, (cx1, cy1, cx2, cy2), hint_size).clamp(-20.0, 20.0)
state = processor.set_image(image[:, cy1:cy2, cx1:cx2])
x1, y1, x2, y2 = target
prompt_box = np.array([x1 - cx1, y1 - cy1, x2 - cx1, y2 - cy1], dtype=np.float32)
masks, _, _ = model.predict_inst(state, box=prompt_box, mask_input=hint[None], multimask_output=False,
return_logits=True)
refined = np.asarray(masks).reshape(-1, cy2 - cy1, cx2 - cx1)[0] > 0
refined = drop_stray_parts(refined, coarse)
accepted = close_up_agrees(refined, coarse, max(image_size) / _PASS1_GRID + 2.0)
return make_segment(concept, score, refined if accepted else coarse, origin=(cx1, cy1)), accepted
def _image_chw(image):
"""One ComfyUI image [H, W, C] -> float [3, H, W] in [0, 1]."""
channels = image.shape[-1]
if channels == 1:
image = image.expand(-1, -1, 3)
elif channels not in (3, 4):
raise ValueError(f"[NH-Nodes] SAM 3 expects an IMAGE with 1, 3 or 4 channels, got {channels}")
return image[..., :3].permute(2, 0, 1).float().clamp(0.0, 1.0)
def segment_images(model, make_processor, images, concepts, threshold=0.5, detail_level="balanced",
max_per_concept=0, device=None, interrupt=None, progress=None):
"""NH_SEGS (one SegsFrame per image) for ``concepts`` found in the ComfyUI IMAGE batch ``images``.
``make_processor(threshold)`` returns a Sam3Processor for ``model`` on ``device``; the model must already be
there. Instances come in concept order, best score first within a concept. ``interrupt()`` runs before each
concept and each close-up (it raises to stop), ``progress(done, total)`` after each concept of each image.
"""
device = device or _model_device(model)
refine = detail_level != "fast"
hint_size = _hint_size(model) if refine else None
frames, kept_pass1, warned_wide = [], 0, False
done, total = 0, len(images) * len(concepts)
with torch.inference_mode(), _autocast(device):
for image in images:
height, width = int(image.shape[0]), int(image.shape[1])
if not warned_wide and max(height, width) > _WIDE_ASPECT * min(height, width):
warned_wide = True
print(f"[NH-Nodes] SAM 3 sees the whole {width}x{height} picture squeezed into a square, so on images "
"wider than 4:1 it can miss small things; split such a picture into tiles for better results")
pixels = _image_chw(image).to(device)
processor = make_processor(threshold)
size = int(getattr(processor, "resolution", 1008))
square = F.interpolate(pixels[None], size=(size, size), mode="bilinear", antialias=True,
align_corners=False)[0].clamp_(0.0, 1.0)
state = processor.set_image(square)
segments = []
for concept in concepts:
if interrupt is not None:
interrupt()
for score, logits, box in find_instances(processor, state, concept, max_per_concept):
if refine:
if interrupt is not None:
interrupt()
seg, refined = _refine(model, processor, pixels, concept, score, logits, box,
(height, width), hint_size)
kept_pass1 += 0 if refined else 1
else:
seg, _ = _upsample(concept, score, logits, box, (height, width))
if seg is not None:
segments.append(seg)
done += 1
if progress is not None:
progress(done, total)
frames.append(SegsFrame(height, width, tuple(segments)))
del pixels, square, state
if kept_pass1:
print(f"[NH-Nodes] SAM 3: kept the first-pass mask for {kept_pass1} instance(s) whose close-up disagreed")
return tuple(frames)
def segment(images, concepts, threshold=0.5, detail_level="balanced", max_per_concept=0, checkpoint=DEFAULT_CHECKPOINT,
keep_model_loaded=False, interrupt=None, progress=None):
"""Load SAM 3 and run ``segment_images`` on ComfyUI's GPU; the weights go back to RAM afterwards unless kept."""
if images.shape[0] == 0:
raise ValueError("[NH-Nodes] SAM 3 got an empty image batch")
model = load_model(checkpoint)
_, processor_cls = ensure_sam3()
device = sam3_device()
try:
move_model(model, device)
return segment_images(
model, lambda value: processor_cls(model, device=device, confidence_threshold=value), images, concepts,
threshold, detail_level, max_per_concept, device, interrupt, progress,
)
finally:
if not keep_model_loaded:
offload(model)
+515
View File
@@ -0,0 +1,515 @@
"""NH_SEGS: the instances the NH vision nodes pass between each other.
An ``NH_SEGS`` value is a tuple with one ``SegsFrame`` per image of a batch. A frame holds the image size and the
``Segment`` instances found in that image. A segment keeps its label, score and box, and only the mask pixels inside
its box, so dozens of instances on an 8K image stay small. Full-size masks are built only when a node outputs a MASK.
Plain torch/numpy, no model: the select, compose, preview and convert nodes and their tests run anywhere.
"""
import fnmatch
import functools
from collections import namedtuple
from dataclasses import dataclass
import cv2
import numpy as np
import torch
from PIL import Image, ImageDraw, ImageFont
SEGS_TYPE = "NH_SEGS"
@dataclass(frozen=True, eq=False)
class Segment:
"""One instance found in an image.
``box`` is (x1, y1, x2, y2) in image pixels with exclusive ends, so the instance covers ``image[y1:y2, x1:x2]``;
it is the tight box around the mask. ``mask`` is a bool [y2 - y1, x2 - x1] CPU tensor over that box.
"""
label: str
score: float
box: tuple
mask: torch.Tensor
source: str = "sam3"
@property
def area(self):
"""Number of mask pixels."""
return int(self.mask.sum())
@property
def center(self):
x1, y1, x2, y2 = self.box
return (x1 + x2) / 2.0, (y1 + y2) / 2.0
@dataclass(frozen=True, eq=False)
class SegsFrame:
"""The instances found in one image of ``height`` x ``width`` pixels, in the order they were found."""
height: int
width: int
segments: tuple = ()
def with_segments(self, segments):
return SegsFrame(self.height, self.width, tuple(segments))
def _comfy_cache_tensors(self):
"""The tensors this frame holds, so ComfyUI's RAM-pressure cache can size it."""
return [seg.mask for seg in self.segments]
def make_segment(label, score, mask, origin=(0, 0), source="sam3"):
"""Segment for the True pixels of the bool [h, w] ``mask`` whose top-left pixel sits at ``origin`` (x, y) of the image.
The stored box is the tight box around those pixels and the stored mask a compact CPU copy of that box. Works on
any device, cropping before the copy to the CPU. Returns None when the mask is empty.
"""
mask = torch.as_tensor(mask)
if mask.dtype != torch.bool:
mask = mask > 0.5
rows = torch.nonzero(mask.any(dim=1)).flatten()
if rows.numel() == 0:
return None
cols = torch.nonzero(mask.any(dim=0)).flatten()
y1, y2 = int(rows[0]), int(rows[-1]) + 1
x1, x2 = int(cols[0]), int(cols[-1]) + 1
crop = mask[y1:y2, x1:x2].to("cpu").clone(memory_format=torch.contiguous_format)
ox, oy = int(origin[0]), int(origin[1])
return Segment(str(label), float(score), (ox + x1, oy + y1, ox + x2, oy + y2), crop, source)
def frame_mask(frame, segments=None):
"""Bool [H, W] union of ``segments`` (every segment of ``frame`` by default)."""
canvas = torch.zeros((frame.height, frame.width), dtype=torch.bool)
for seg in frame.segments if segments is None else segments:
x1, y1, x2, y2 = seg.box
canvas[y1:y2, x1:x2] |= seg.mask
return canvas
def segs_union(segs):
"""MASK [B, H, W] float: the union of every instance, one mask per image."""
return torch.stack([frame_mask(frame) for frame in segs]).float()
def check_memory(nbytes, what):
"""Raise MemoryError with advice when ``nbytes`` would take more than half of the RAM that is free now."""
try:
import psutil
available = psutil.virtual_memory().available
except Exception: # no psutil: nothing to compare with
return
if nbytes > available // 2:
raise MemoryError(
f"[NH-Nodes] {what} would need {nbytes / 2**30:.1f} GiB but only {available / 2**30:.1f} GiB of RAM is "
"free. Keep fewer instances with Segs Select, or combine them into one mask with Segs Compose."
)
def instance_masks(segs):
"""(MASK [N, H, W] float with one full-size mask per instance of every image, in order; the labels).
With no instance at all the MASK holds a single empty mask, as nodes downstream may not accept an empty batch.
Each full-size mask takes H x W x 4 bytes (about 145 MB on an 8K image), so a MemoryError explains what to do
when they would not fit in RAM.
"""
labels = [seg.label for frame in segs for seg in frame.segments]
height, width = segs[0].height, segs[0].width
check_memory(max(1, len(labels)) * height * width * 4, f"{len(labels)} full-size instance masks")
masks = torch.zeros((max(1, len(labels)), height, width))
index = 0
for frame in segs:
for seg in frame.segments:
x1, y1, x2, y2 = seg.box
masks[index, y1:y2, x1:x2] = seg.mask.float()
index += 1
return masks, labels
def count_segments(segs):
return sum(len(frame.segments) for frame in segs)
# ---------------------------------------------------------------------------
# Labels
# ---------------------------------------------------------------------------
def normalize_label(text):
"""Lower case, '_' as a space, single spaces: "Red_Car " and "red car" both become "red car"."""
return " ".join(str(text).replace("_", " ").lower().split())
def label_matches(label, pattern):
"""True when ``label`` matches ``pattern``, ignoring case; the pattern may use * and ? wildcards."""
return fnmatch.fnmatchcase(normalize_label(label), normalize_label(pattern))
def parse_label_list(text):
"""Commas and new lines separate entries: "person, red car" -> ["person", "red car"].
Blank entries and lines starting with '#' are dropped; a repeated entry (ignoring case and spacing) is kept once,
with its first spelling.
"""
entries, seen = [], set()
for line in str(text or "").splitlines():
if line.strip().startswith("#"):
continue
for part in line.split(","):
entry = " ".join(part.split())
key = normalize_label(entry)
if entry and key not in seen:
seen.add(key)
entries.append(entry)
return entries
# ---------------------------------------------------------------------------
# Select
# ---------------------------------------------------------------------------
SORT_ORDERS = ("score", "area", "left_to_right", "right_to_left", "top_to_bottom", "bottom_to_top",
"center_first", "as_found")
def sort_segments(segments, order, frame):
"""``segments`` in ``order`` (one of SORT_ORDERS); positions use each box's centre. Ties keep their found order."""
segments = list(segments)
if order == "score":
return sorted(segments, key=lambda s: -s.score)
if order == "area":
return sorted(segments, key=lambda s: -s.area)
if order == "left_to_right":
return sorted(segments, key=lambda s: s.center[0])
if order == "right_to_left":
return sorted(segments, key=lambda s: -s.center[0])
if order == "top_to_bottom":
return sorted(segments, key=lambda s: s.center[1])
if order == "bottom_to_top":
return sorted(segments, key=lambda s: -s.center[1])
if order == "center_first":
cx, cy = frame.width / 2.0, frame.height / 2.0
return sorted(segments, key=lambda s: (s.center[0] - cx) ** 2 + (s.center[1] - cy) ** 2)
return segments
def select_segments(frame, labels=(), min_score=0.0, min_area=0.0, max_area=1.0, order="score", start=0, count=0):
"""Frame with the segments that pass the filters, sorted by ``order``, from position ``start`` (0 = first).
``labels`` are patterns (any one must match; none = every label). Areas are fractions of the image. ``count`` = 0
keeps every segment from ``start`` on.
"""
total = max(1, frame.height * frame.width)
kept = []
for seg in frame.segments:
if labels and not any(label_matches(seg.label, pattern) for pattern in labels):
continue
if seg.score < min_score:
continue
if not min_area <= seg.area / total <= max_area:
continue
kept.append(seg)
kept = sort_segments(kept, order, frame)
start = max(0, int(start))
kept = kept[start:] if count <= 0 else kept[start:start + int(count)]
return frame.with_segments(kept)
# ---------------------------------------------------------------------------
# Compose: a small boolean language over labels
# ---------------------------------------------------------------------------
class ExpressionError(ValueError):
"""An NH_SEGS mask expression that cannot be read."""
_OPERATORS = {"+": "|", "|": "|", "-": "-", "&": "&", "~": "~", "!": "~", "(": "(", ")": ")"}
_WORD_OPERATORS = {"or": "|", "and": "&", "not": "~"}
_KEYWORDS = ("all", "image", "none")
_QUOTES = "\"'"
def _is_word_char(ch):
return ch.isalnum() or ch == "_"
def _tokenize(text):
"""[(kind, value, position, written)] with kinds "op", "keyword", "label"; positions count from 1.
A quote opens a quoted label only where a token starts, and a '-' between two word characters is part of the
word, so ``women's`` and ``t-shirt`` are plain labels while ``person - hair`` subtracts.
"""
tokens, i, n = [], 0, len(text)
while i < n:
ch = text[i]
if ch.isspace():
i += 1
elif ch in _OPERATORS:
tokens.append(("op", _OPERATORS[ch], i + 1, ch))
i += 1
elif ch in _QUOTES:
end = text.find(ch, i + 1)
if end < 0:
raise ExpressionError(f"quote opened at position {i + 1} is never closed")
tokens.append(("label", text[i + 1:end], i + 1, text[i:end + 1]))
i = end + 1
else:
j = i
while j < n and not text[j].isspace():
hyphen_in_word = (text[j] == "-" and _is_word_char(text[j - 1])
and j + 1 < n and _is_word_char(text[j + 1]))
if text[j] in _OPERATORS and not hyphen_in_word:
break
j += 1
word = text[i:j]
lowered = word.lower()
if lowered in _WORD_OPERATORS:
tokens.append(("op", _WORD_OPERATORS[lowered], i + 1, word))
elif lowered in _KEYWORDS:
tokens.append(("keyword", lowered, i + 1, word))
else:
tokens.append(("label", word, i + 1, word))
i = j
return tokens
class _Parser:
"""expr := term (('|' | '-') term)* ; term := unary ('&' unary)* ; unary := '~' unary | atom ;
atom := label | keyword | '(' expr ')'. Builds a tuple tree evaluated by ``evaluate``."""
def __init__(self, text):
self.tokens = _tokenize(text)
self.pos = 0
def _peek(self):
return self.tokens[self.pos] if self.pos < len(self.tokens) else None
def _where(self):
token = self._peek()
return f"'{token[3]}' at position {token[2]}" if token else "the end"
def parse(self):
if not self.tokens:
raise ExpressionError("the expression is empty; use 'all' for every instance")
tree = self._expr()
if self._peek() is not None:
raise ExpressionError(f"unexpected {self._where()}")
return tree
def _expr(self):
tree = self._term()
while (token := self._peek()) is not None and token[0] == "op" and token[1] in ("|", "-"):
self.pos += 1
tree = (token[1], tree, self._term())
return tree
def _term(self):
tree = self._unary()
while (token := self._peek()) is not None and token[0] == "op" and token[1] == "&":
self.pos += 1
tree = ("&", tree, self._unary())
return tree
def _unary(self):
token = self._peek()
if token is not None and token[0] == "op" and token[1] == "~":
self.pos += 1
return ("~", self._unary())
return self._atom()
def _atom(self):
token = self._peek()
if token is None:
raise ExpressionError("the expression ends where a label, 'all', 'image', 'none' or '(' was expected")
kind, value, position, written = token
self.pos += 1
if kind == "label":
return ("label", value)
if kind == "keyword":
return ("keyword", value)
if value == "(":
tree = self._expr()
closing = self._peek()
if closing is None or closing[:2] != ("op", ")"):
raise ExpressionError(f"'(' at position {position} is not closed (found {self._where()})")
self.pos += 1
return tree
raise ExpressionError(f"unexpected '{written}' at position {position}")
@functools.lru_cache(maxsize=64)
def parse_expression(text):
"""Parse a mask expression (see ``evaluate``) into a tree; raises ExpressionError with the position on mistakes."""
return _Parser(str(text)).parse()
def expression_labels(tree):
"""Label patterns used in a parsed expression, in order."""
if tree[0] == "label":
return [tree[1]]
if tree[0] == "keyword":
return []
return [label for child in tree[1:] for label in expression_labels(child)]
def evaluate(tree, frame):
"""Bool [H, W] mask of a parsed expression for one frame.
A label (wildcards allowed; quotes or '_' for spaces; a '-' inside a word is part of it) is the union of the
matching instances,
``all`` every instance, ``image`` the whole frame, ``none`` nothing. ``a | b`` (also ``+``, ``or``) is the union,
``a - b`` the difference, ``a & b`` (``and``) the intersection, ``~a`` (``!``, ``not``) everything outside ``a``.
"""
kind = tree[0]
if kind == "label":
return frame_mask(frame, [seg for seg in frame.segments if label_matches(seg.label, tree[1])])
if kind == "keyword":
if tree[1] == "all":
return frame_mask(frame)
if tree[1] == "image":
return torch.ones((frame.height, frame.width), dtype=torch.bool)
return torch.zeros((frame.height, frame.width), dtype=torch.bool)
if kind == "~":
return ~evaluate(tree[1], frame)
left, right = evaluate(tree[1], frame), evaluate(tree[2], frame)
if kind == "|":
return left | right
if kind == "&":
return left & right
return left & ~right
def expression_masks(tree, segs):
"""MASK [B, H, W] float of a parsed expression, one mask per image of ``segs``."""
return torch.stack([evaluate(tree, frame) for frame in segs]).float()
# ---------------------------------------------------------------------------
# Impact Pack SEGS
# ---------------------------------------------------------------------------
# The field names of Impact Pack's SEG, so its detailers read these items; NH-Nodes does not import Impact Pack.
IMPACT_SEG = namedtuple("SEG", ["cropped_image", "cropped_mask", "confidence", "crop_region", "bbox", "label",
"control_net_wrapper"], defaults=[None])
def _centred_span(lo, hi, factor, limit):
"""[start, end) of ``factor`` times the length of [lo, hi), centred on it and slid inside [0, limit).
It is never shorter than [lo, hi) and only shorter than asked when the image is, so it always contains [lo, hi).
"""
size = min(limit, max(hi - lo, round((hi - lo) * factor)))
start = min(max(0, round((lo + hi - size) / 2)), limit - size)
return start, start + size
def detail_crop_region(width, height, box, crop_factor):
"""[x1, y1, x2, y2] a detailer works on around ``box``: ``crop_factor`` times its size, centred, inside the image."""
rx1, rx2 = _centred_span(box[0], box[2], crop_factor, width)
ry1, ry2 = _centred_span(box[1], box[3], crop_factor, height)
return [rx1, ry1, rx2, ry2]
def to_impact_segs(frame, crop_factor=3.0):
"""Impact Pack SEGS ``((height, width), [SEG, ...])`` for one frame; each mask is placed in its crop region."""
items = []
for seg in frame.segments:
region = detail_crop_region(frame.width, frame.height, seg.box, crop_factor)
rx1, ry1, rx2, ry2 = region
x1, y1, x2, y2 = seg.box
cropped = np.zeros((ry2 - ry1, rx2 - rx1), dtype=np.float32)
cropped[y1 - ry1:y2 - ry1, x1 - rx1:x2 - rx1] = seg.mask.numpy()
items.append(IMPACT_SEG(None, cropped, float(seg.score), region, [x1, y1, x2, y2], seg.label, None))
return (frame.height, frame.width), items
# ---------------------------------------------------------------------------
# Preview
# ---------------------------------------------------------------------------
_PALETTE = (
(230, 25, 75), (60, 180, 75), (255, 225, 25), (0, 130, 200), (245, 130, 48), (145, 30, 180),
(70, 240, 240), (240, 50, 230), (210, 245, 60), (250, 190, 212), (0, 128, 128), (170, 110, 40),
)
@functools.lru_cache(maxsize=16)
def _label_font(size):
for name in ("arial.ttf", "DejaVuSans.ttf"): # both cover Vietnamese
try:
return ImageFont.truetype(name, size)
except Exception:
continue
try:
return ImageFont.load_default(size=size)
except TypeError: # Pillow < 10.1
return ImageFont.load_default()
def _free_spot(x, y, width, height, placed, limit):
"""``y`` moved down (or up near the bottom ``limit``) until a ``width`` x ``height`` label at ``x`` overlaps none
of the ``placed`` (x1, y1, x2, y2) rectangles; labels of instances sharing a top edge (person, hair) then stack."""
for _ in range(len(placed)):
hit = next((r for r in placed if x < r[2] and r[0] < x + width and y < r[3] and r[1] < y + height), None)
if hit is None:
break
y = hit[3] + 1 if hit[3] + 1 + height <= limit else max(0, hit[1] - height - 1)
return y
def render_preview(image, frame, fill=0.45):
"""uint8 HxWx3 copy of ``image`` with each instance tinted, outlined and labelled "label score"."""
canvas = np.ascontiguousarray(image[:, :, :3]).copy()
height, width = canvas.shape[:2]
if not frame.segments:
return canvas
thickness = max(1, round(min(height, width) / 700))
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (2 * thickness + 1, 2 * thickness + 1))
for index, seg in enumerate(frame.segments):
color = np.array(_PALETTE[index % len(_PALETTE)], dtype=np.float32)
x1, y1, x2, y2 = seg.box
region = canvas[y1:y2, x1:x2]
inside = seg.mask.numpy()
region[inside] = (region[inside] * (1.0 - fill) + color * fill).astype(np.uint8)
padded = np.pad(inside.astype(np.uint8), thickness) # so a box edge counts as an outline too
edge = (padded > 0) & (cv2.erode(padded, kernel) == 0)
region[edge[thickness:-thickness, thickness:-thickness]] = color.astype(np.uint8)
font = _label_font(max(12, round(min(height, width) / 45)))
picture = Image.fromarray(canvas)
draw = ImageDraw.Draw(picture)
placed = [] # label rectangles drawn so far, so a label never hides another one
for index, seg in enumerate(frame.segments):
color = _PALETTE[index % len(_PALETTE)]
text = f"{seg.label} {seg.score:.2f}"
left, top, right, bottom = draw.textbbox((0, 0), text, font=font)
pad = max(2, (bottom - top) // 6)
tw, th = right - left + 2 * pad, bottom - top + 2 * pad
# centred over the instance's topmost pixels (a head, a roof), not at a box corner that may be far from it
top_columns = torch.nonzero(seg.mask[0]).flatten()
anchor = seg.box[0] + int(top_columns[top_columns.numel() // 2]) if top_columns.numel() else seg.box[0]
x = min(max(0, anchor - tw // 2), max(0, width - tw))
y = _free_spot(x, seg.box[1] - th if seg.box[1] - th >= 0 else seg.box[1], tw, th, placed, height)
placed.append((x, y, x + tw, y + th))
draw.rectangle((x, y, x + tw, y + th), fill=color)
dark_text = (0.299 * color[0] + 0.587 * color[1] + 0.114 * color[2]) > 150
draw.text((x + pad - left, y + pad - top), text, font=font, fill=(0, 0, 0) if dark_text else (255, 255, 255))
return np.asarray(picture)
def preview_batch(image, segs):
"""IMAGE batch with every instance of ``segs`` drawn on its image (tinted, outlined, labelled)."""
if len(segs) != image.shape[0]:
raise ValueError(
f"[NH-Nodes] The image batch has {image.shape[0]} image(s) but the segs belong to {len(segs)}; "
"connect the image the segs were found in."
)
pictures = []
for picture, frame in zip(image, segs):
if (picture.shape[0], picture.shape[1]) != (frame.height, frame.width):
raise ValueError(
f"[NH-Nodes] The image is {picture.shape[1]}x{picture.shape[0]} but the segs were found on a "
f"{frame.width}x{frame.height} image; connect the image the segs were found in."
)
rgb = picture[..., :3] if picture.shape[-1] >= 3 else picture[..., :1].expand(-1, -1, 3)
pixels = (rgb.float().clamp(0.0, 1.0) * 255.0).round().to(torch.uint8).cpu().numpy()
pictures.append(torch.from_numpy(render_preview(pixels, frame).astype(np.float32) / 255.0))
return torch.stack(pictures)
+24
View File
@@ -3,6 +3,30 @@
All notable changes to NH-Nodes. Versions follow the Comfy Registry releases.
"Behaviour change" marks fixes that can change the output of an existing workflow.
## 1.7.0 — SAM 3 segmentation and instance nodes (2026-10-18)
### Added
Six nodes under **NH-Nodes → Vision** (75 nodes in total) and a new data type, `NH_SEGS`. Nothing in the existing nodes changes.
- `SAM3 Segment (NH)`: finds every instance of the concepts you name (short English noun phrases, one per line or separated by commas) with Meta's SAM 3. Outputs the instances (`segs`), their union `mask` and the `count`; `Segs Preview (NH)` shows what it found. `detail_level` `balanced` (default) segments each instance a second time on a close-up cut from the full-resolution image, so small and medium objects get crisp edges even on 4K to 8K photos; `fast` keeps the whole-image pass only. Other inputs: `threshold`, `max_per_concept`, `checkpoint` and `keep_model_loaded`.
- `NH_SEGS`: for every image of a batch, a list of instances with a label, a score, a tight box and the mask inside that box only, so dozens of instances on an 8K photo stay light. Full-size masks are built only when a node outputs a `MASK`.
- `Segs Select (NH)`: keeps instances by label (`*` and `?` wildcards), score and size, sorts them (score, area, position, centre first, as found) and takes a slice, for example the second person from the left.
- `Segs Compose (NH)`: one mask from a formula over labels: `|` `+` `or` (union), `-` (minus), `&` `and` (overlap), `~` `!` `not` (everything except), parentheses, and `all`, `image`, `none`. `~person` is the background, `(wall | ceiling) - window` the walls without the windows. A hyphen inside a word belongs to the label (`t-shirt` needs no quotes), so put spaces around a minus: `person - hair`.
- `Segs To Masks (NH)`: one mask per instance, the union per image, the labels and the count. It stops with a message when the masks would need more than about half of the free RAM, for example hundreds of instances on an 8K photo.
- `Segs To Impact SEGS (NH)`: Impact Pack's `SEGS` for one image of the batch (`crop_factor` 3.0 like Impact Pack). Impact Pack is not needed for anything else in NH-Nodes.
- `Segs Preview (NH)`: draws the instances (tint, outline, label and score) on the picture they were found in.
### Notes
- SAM3 Segment needs an NVIDIA GPU with CUDA: the official sam3 code builds part of the model on the GPU, so there is no CPU mode.
- NH-Nodes does not install or download anything for it. You install Meta's `sam3` package yourself (the Vision guide gives the command, pinned to the commit NH-Nodes was tested with; newer commits with the same image API also work), and the weights are gated on Hugging Face: request access, accept Meta's SAM License and put `sam3.pt` in `models/sam3` (or a folder registered as `sam3` in `extra_model_paths.yaml`). The folder is shared with other SAM 3 packs, so an existing copy is found. The licence is in `THIRD_PARTY_NOTICES.md`.
- The model stays in system memory between runs and moves to the GPU only while the node runs, unless `keep_model_loaded` is on.
- `detail_level` has two levels for now, `balanced` and `fast`.
- SAM 3 describes whatever it looks at on a grid of about 288 cells. A subject that fills most of an 8K frame therefore gets edges at about 1/300 of its size: fine for the outline of a person, soft on hair, lace and fur. Hair-level edges are not part of this release.
- SAM 3 first sees the whole picture squeezed into a square, so on a picture wider or taller than 4:1 it can miss small things. The console says so; split such a picture into tiles with `Image Tile (NH)`.
- A file in the `sam3` folder that is not a SAM 3 checkpoint, or an incomplete download, is refused with the download advice before anything runs.
### Planned
Not part of this release, and the names may change: scene parsing that labels the whole picture (sky, wall, floor…), automatic concept tagging with ready presets and a report, matte refinement for hair and fur edges (it will add a `max` detail level), and parsing of each person's parts.
## 1.6.2 — Load images matching picks the right group
- `Load images matching` (behaviour change): when the source and the matching library share parent folders (for example
File diff suppressed because it is too large Load Diff
+241
View File
@@ -0,0 +1,241 @@
"""Render a ComfyUI-style SVG card for every NH-Nodes node from its INPUT_TYPES / RETURN_TYPES.
Run with ComfyUI's Python from the ComfyUI root:
python custom_nodes/NH-Nodes/docs/_tools/build_node_cards.py [output_dir]
Cards are generated from the live node definitions, so they never drift from the code.
"""
import importlib
import json
import os
import re
import sys
from html import escape
COMFY_ROOT = os.getcwd()
sys.path[:0] = [COMFY_ROOT, os.path.join(COMFY_ROOT, "custom_nodes")]
import torch # noqa: E402
if not torch.cuda.is_available():
from comfy.cli_args import args # noqa: E402
args.cpu = True
PACK_DIR = os.path.join(COMFY_ROOT, "custom_nodes", "NH-Nodes")
OUT_DIR = sys.argv[1] if len(sys.argv) > 1 else os.path.join(PACK_DIR, "docs", "assets", "nodes")
# ComfyUI default dark palette (frontend 1.55) for link types; NH custom types get amber.
TYPE_COLORS = {
"IMAGE": "#64B5F6", "MASK": "#81C784", "LATENT": "#FF9CF9", "MODEL": "#B39DDB",
"CLIP": "#FFD500", "VAE": "#FF6E6E", "CONDITIONING": "#FFA931", "CONTROL_NET": "#6EE7B7",
"INT": "#9CC2E5", "FLOAT": "#9CC2E5", "STRING": "#C5E1A5", "BOOLEAN": "#F48FB1", "*": "#CFD8DC",
}
CUSTOM_TYPE_COLOR = "#E0B272"
WIDGET_TYPES = {"INT", "FLOAT", "STRING", "BOOLEAN"}
W = 360
PAD = 12
TITLE_H = 46
ROW_H = 22
WIDGET_H = 24
WIDGET_GAP = 5
FONT = "Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif"
MONO = "'JetBrains Mono', Consolas, 'Courier New', monospace"
FILE_EXTENSIONS = (".png", ".jpg", ".jpeg", ".webp", ".bmp", ".tif", ".tiff", ".gif", ".safetensors", ".ckpt",
".pt", ".pth", ".bin", ".gguf", ".onnx", ".sft")
def is_file_combo(options, opts):
"""Combos listing files from the user's folders (inputs, models): never publish those names."""
if opts.get("image_upload") or opts.get("video_upload"):
return True
return any(str(option).lower().endswith(FILE_EXTENSIONS) for option in options)
def type_name(spec_type):
if isinstance(spec_type, (list, tuple)):
return "COMBO"
return str(spec_type)
def type_color(name):
return TYPE_COLORS.get(name, CUSTOM_TYPE_COLOR)
def short(value, limit=24):
text = str(value).replace("\n", " ⏎ ")
return text if len(text) <= limit else text[: limit - 1] + "…"
def fmt_number(value, opts):
if isinstance(value, float):
step = opts.get("step", 0.01) or 0.01
decimals = max(0, min(4, len(str(step).split(".")[1]) if "." in str(step) else 0))
return f"{value:.{decimals}f}"
return str(value)
def collapse_numbered(entries):
"""Fold long numbered families (lora_1..lora_64, enable_1..) into first two + a summary row."""
families = {}
for name, *_ in entries:
match = re.match(r"^(.*?)(\d+)$", name)
if match:
families.setdefault(match.group(1), []).append(int(match.group(2)))
folded = {prefix: max(nums) for prefix, nums in families.items() if len(nums) > 4}
result, emitted = [], set()
for entry in entries:
match = re.match(r"^(.*?)(\d+)$", entry[0])
if match and match.group(1) in folded:
prefix, num = match.group(1), int(match.group(2))
if num <= 2:
result.append(entry)
elif prefix not in emitted:
emitted.add(prefix)
result.append((f"{prefix}3 … {prefix}{folded[prefix]}", "FOLD", {}, entry[3]))
continue
result.append(entry)
return result
def node_entries(cls):
spec = cls.INPUT_TYPES()
entries = []
for section in ("required", "optional"):
for name, value in (spec.get(section) or {}).items():
spec_type = value[0] if isinstance(value, (list, tuple)) and value else value
opts = value[1] if isinstance(value, (list, tuple)) and len(value) > 1 and isinstance(value[1], dict) else {}
entries.append((name, spec_type, opts, section == "optional"))
return collapse_numbered(entries)
def split_entries(entries):
sockets, widgets = [], []
for name, spec_type, opts, optional in entries:
tname = type_name(spec_type)
is_widget = tname == "COMBO" or tname == "FOLD" or (tname in WIDGET_TYPES and not opts.get("forceInput"))
(widgets if is_widget else sockets).append((name, spec_type, opts, optional))
return sockets, widgets
def widget_value(spec_type, opts):
tname = type_name(spec_type)
if tname == "COMBO":
options = list(spec_type)
if is_file_combo(options, opts):
return "(choose a file)", ""
default = opts.get("default", options[0] if options else "")
return (short(default) if default != "" else "—"), f"{len(options)} options" if len(options) > 1 else ""
default = opts.get("default", "")
if tname == "BOOLEAN":
return ("true" if default else "false"), ""
if tname in ("INT", "FLOAT"):
return fmt_number(default, opts), ""
if tname == "STRING":
return short(default, 30) if default != "" else "", "multiline" if opts.get("multiline") else ""
return short(default), ""
def svg_card(node_id, display, category, cls):
entries = node_entries(cls)
sockets, widgets = split_entries(entries)
return_types = [str(t) for t in getattr(cls, "RETURN_TYPES", ())]
return_names = list(getattr(cls, "RETURN_NAMES", None) or return_types)
is_list = list(getattr(cls, "OUTPUT_IS_LIST", ()) or [])
output_node = getattr(cls, "OUTPUT_NODE", False)
io_rows = max(len(sockets), len(return_types))
multiline_rows = sum(1 for _, t, o, _ in widgets if type_name(t) == "STRING" and o.get("multiline"))
widgets_h = len(widgets) * (WIDGET_H + WIDGET_GAP) + multiline_rows * 22
height = TITLE_H + 10 + io_rows * ROW_H + (8 if io_rows and widgets else 0) + widgets_h + PAD
parts = [
f'<svg xmlns="http://www.w3.org/2000/svg" width="{W}" height="{height}" viewBox="0 0 {W} {height}" '
f'font-family="{FONT}" role="img" aria-label="{escape(display)} node">',
f'<rect x="0.5" y="0.5" width="{W - 1}" height="{height - 1}" rx="10" fill="#26282c" stroke="#45484f"/>',
f'<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h{W - 21} a10 10 0 0 1 10 10 v{TITLE_H - 10} h-{W - 1} z" fill="#34373d"/>',
f'<circle cx="{PAD + 5}" cy="17" r="4.5" fill="{"#F6AD55" if output_node else "#8FD18F"}"/>',
f'<text x="{PAD + 16}" y="22" font-size="14" font-weight="600" fill="#EEF0F3">{escape(short(display, 40))}</text>',
f'<text x="{PAD + 16}" y="38" font-size="10.5" fill="#9AA0A8" font-family="{MONO}">'
f'{escape(node_id)} · {escape(category.replace("NH-Nodes/", ""))}</text>',
]
y = TITLE_H + 10
for index in range(io_rows):
cy = y + index * ROW_H + ROW_H / 2
if index < len(sockets):
name, spec_type, opts, optional = sockets[index]
tname = type_name(spec_type)
color = type_color(tname)
parts.append(f'<circle cx="{PAD}" cy="{cy}" r="5" fill="{color}" stroke="#1b1c1f" stroke-width="1.5"/>')
label = escape(name) + (' <tspan fill="#7d838c" font-style="italic">(optional)</tspan>' if optional else "")
parts.append(f'<text x="{PAD + 12}" y="{cy + 4}" font-size="12" fill="#D7DADF">{label}</text>')
if index < len(return_types):
tname = return_types[index]
color = type_color(tname)
name = return_names[index] if index < len(return_names) else tname
list_mark = " ≡" if index < len(is_list) and is_list[index] else ""
parts.append(f'<text x="{W - PAD - 12}" y="{cy + 4}" font-size="12" fill="#D7DADF" text-anchor="end">'
f'{escape(name)}{list_mark}</text>')
parts.append(f'<circle cx="{W - PAD}" cy="{cy}" r="5" fill="{color}" stroke="#1b1c1f" stroke-width="1.5"/>')
y += io_rows * ROW_H + (8 if io_rows and widgets else 0)
for name, spec_type, opts, optional in widgets:
tname = type_name(spec_type)
multiline = tname == "STRING" and opts.get("multiline")
h = WIDGET_H + (22 if multiline else 0)
parts.append(f'<rect x="{PAD}" y="{y}" width="{W - 2 * PAD}" height="{h}" rx="7" fill="#1d1f22" stroke="#3a3d43"/>')
if tname == "FOLD":
parts.append(f'<text x="{W / 2}" y="{y + 16}" font-size="11" fill="#9AA0A8" text-anchor="middle" '
f'font-style="italic">{escape(name)}</text>')
y += h + WIDGET_GAP
continue
value, hint = widget_value(spec_type, opts)
label = escape(name) + (' <tspan fill="#6f747c" font-style="italic">opt</tspan>' if optional else "")
if multiline:
parts.append(f'<text x="{PAD + 10}" y="{y + 16}" font-size="11.5" fill="#9AA0A8">{label}</text>')
parts.append(f'<text x="{PAD + 10}" y="{y + 36}" font-size="11.5" fill="#E6E8EB" font-family="{MONO}">'
f'{escape(value) or "…"}</text>')
elif tname == "BOOLEAN":
on = value == "true"
parts.append(f'<text x="{PAD + 10}" y="{y + 16}" font-size="11.5" fill="#9AA0A8">{label}</text>')
tx = W - PAD - 40
parts.append(f'<rect x="{tx}" y="{y + 6}" width="30" height="13" rx="6.5" fill="{"#4C8BF5" if on else "#4a4d53"}"/>')
parts.append(f'<circle cx="{tx + (23 if on else 7)}" cy="{y + 12.5}" r="5" fill="#F5F6F7"/>')
else:
arrows = tname in ("INT", "FLOAT", "COMBO")
if arrows:
parts.append(f'<text x="{PAD + 8}" y="{y + 16}" font-size="10" fill="#6f747c">◀</text>')
parts.append(f'<text x="{W - PAD - 16}" y="{y + 16}" font-size="10" fill="#6f747c">▶</text>')
lx = PAD + (22 if arrows else 10)
rx = W - PAD - (22 if arrows else 10)
parts.append(f'<text x="{lx}" y="{y + 16}" font-size="11.5" fill="#9AA0A8">{label}</text>')
parts.append(f'<text x="{rx}" y="{y + 16}" font-size="11.5" fill="#E6E8EB" text-anchor="end">{escape(value)}</text>')
y += h + WIDGET_GAP
parts.append("</svg>")
return "\n".join(parts) + "\n"
def main():
pack = importlib.import_module("NH-Nodes")
os.makedirs(OUT_DIR, exist_ok=True)
index = []
for node_id, cls in sorted(pack.NODE_CLASS_MAPPINGS.items()):
display = pack.NODE_DISPLAY_NAME_MAPPINGS.get(node_id, node_id)
category = getattr(cls, "CATEGORY", "")
svg = svg_card(node_id, display, category, cls)
with open(os.path.join(OUT_DIR, f"{node_id}.svg"), "w", encoding="utf-8", newline="\n") as handle:
handle.write(svg)
index.append({"id": node_id, "name": display, "category": category,
"deprecated": bool(getattr(cls, "DEPRECATED", False))})
with open(os.path.join(OUT_DIR, "index.json"), "w", encoding="utf-8", newline="\n") as handle:
json.dump(index, handle, ensure_ascii=False, indent=1)
print(f"wrote {len(index)} cards to {OUT_DIR}")
if __name__ == "__main__":
main()
File diff suppressed because it is too large Load Diff
+82
View File
@@ -0,0 +1,82 @@
"""Dump every NH node's live definition (inputs with defaults/ranges/options/tooltips, outputs) to JSON.
Run with ComfyUI's Python from the ComfyUI root: python dump_node_spec.py <out.json>
"""
import importlib
import json
import os
import sys
sys.path[:0] = [os.getcwd(), os.path.join(os.getcwd(), "custom_nodes")]
import torch # noqa: E402
if not torch.cuda.is_available():
from comfy.cli_args import args # noqa: E402
args.cpu = True
pack = importlib.import_module("NH-Nodes")
FILE_EXTENSIONS = (".png", ".jpg", ".jpeg", ".webp", ".bmp", ".tif", ".tiff", ".gif", ".safetensors", ".ckpt",
".pt", ".pth", ".bin", ".gguf", ".onnx", ".sft")
def is_file_combo(options, opts):
"""Combos listing files from the user's folders (inputs, models): never publish those names."""
if opts.get("image_upload") or opts.get("video_upload"):
return True
return any(str(option).lower().endswith(FILE_EXTENSIONS) for option in options)
def jsonable(value):
try:
json.dumps(value)
return value
except TypeError:
return str(value)
spec = []
for node_id, cls in sorted(pack.NODE_CLASS_MAPPINGS.items()):
inputs = []
for section, entries in (cls.INPUT_TYPES() or {}).items():
if section == "hidden":
continue
for name, value in (entries or {}).items():
spec_type = value[0] if isinstance(value, (list, tuple)) and value else value
opts = value[1] if isinstance(value, (list, tuple)) and len(value) > 1 and isinstance(value[1], dict) else {}
entry = {"name": name, "section": section}
if isinstance(spec_type, (list, tuple)):
entry["type"] = "COMBO"
options = [str(o) for o in spec_type]
if is_file_combo(options, opts):
entry["options"] = ["(files from the user's folder)"]
opts = {k: v for k, v in opts.items() if k != "default"}
else:
entry["options"] = options if len(options) <= 40 else options[:40] + [f"... ({len(options)} total)"]
else:
entry["type"] = str(spec_type)
for key in ("default", "min", "max", "step", "multiline", "tooltip", "forceInput", "lazy"):
if key in opts:
entry[key] = jsonable(opts[key])
inputs.append(entry)
names = list(getattr(cls, "RETURN_NAMES", None) or getattr(cls, "RETURN_TYPES", ()))
types = [str(t) for t in getattr(cls, "RETURN_TYPES", ())]
is_list = list(getattr(cls, "OUTPUT_IS_LIST", ()) or [])
outputs = [{"name": n, "type": t, "is_list": bool(is_list[i]) if i < len(is_list) else False}
for i, (n, t) in enumerate(zip(names, types))]
spec.append({
"id": node_id,
"name": pack.NODE_DISPLAY_NAME_MAPPINGS.get(node_id, node_id),
"category": getattr(cls, "CATEGORY", ""),
"module": cls.__module__.replace("NH-Nodes.", ""),
"description": getattr(cls, "DESCRIPTION", "") or (cls.__doc__ or "").strip(),
"output_node": bool(getattr(cls, "OUTPUT_NODE", False)),
"deprecated": bool(getattr(cls, "DEPRECATED", False)),
"inputs": inputs,
"outputs": outputs,
})
with open(sys.argv[1], "w", encoding="utf-8") as handle:
json.dump(spec, handle, ensure_ascii=False, indent=1)
print(f"{len(spec)} nodes -> {sys.argv[1]}")
Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

@@ -0,0 +1,42 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="287" viewBox="0 0 360 287" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="NH Agnostic Image Generator node">
<rect x="0.5" y="0.5" width="359" height="286" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">NH Agnostic Image Generator</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_AgnosticImageGenerator · Image</text>
<circle cx="12" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">image</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">agnostic_img</text>
<circle cx="348" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<circle cx="12" cy="89.0" r="5" fill="#81C784" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="93.0" font-size="12" fill="#D7DADF">mask</text>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">masked_img</text>
<circle cx="348" cy="89.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">composite</text>
<circle cx="348" cy="111.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="130" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="146" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="146" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="146" font-size="11.5" fill="#9AA0A8">fill_mode</text>
<text x="326" y="146" font-size="11.5" fill="#E6E8EB" text-anchor="end">gray</text>
<rect x="12" y="159" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="175" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="175" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="175" font-size="11.5" fill="#9AA0A8">blur_radius</text>
<text x="326" y="175" font-size="11.5" fill="#E6E8EB" text-anchor="end">65</text>
<rect x="12" y="188" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="204" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="204" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="204" font-size="11.5" fill="#9AA0A8">noise_strength</text>
<text x="326" y="204" font-size="11.5" fill="#E6E8EB" text-anchor="end">1.00</text>
<rect x="12" y="217" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="233" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="233" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="233" font-size="11.5" fill="#9AA0A8">gray_value</text>
<text x="326" y="233" font-size="11.5" fill="#E6E8EB" text-anchor="end">0.50</text>
<rect x="12" y="246" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="262" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="262" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="262" font-size="11.5" fill="#9AA0A8">feathering</text>
<text x="326" y="262" font-size="11.5" fill="#E6E8EB" text-anchor="end">0</text>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

+36
View File
@@ -0,0 +1,36 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="347" viewBox="0 0 360 347" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Any List Split (NH) node">
<rect x="0.5" y="0.5" width="359" height="346" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Any List Split (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_AnyListSplit · Batch</text>
<circle cx="12" cy="67.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">items</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">All ≡</text>
<circle cx="348" cy="67.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">index 1</text>
<circle cx="348" cy="89.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">index 2</text>
<circle cx="348" cy="111.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="137.0" font-size="12" fill="#D7DADF" text-anchor="end">index 3</text>
<circle cx="348" cy="133.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="159.0" font-size="12" fill="#D7DADF" text-anchor="end">index 4</text>
<circle cx="348" cy="155.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="181.0" font-size="12" fill="#D7DADF" text-anchor="end">index 5</text>
<circle cx="348" cy="177.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="203.0" font-size="12" fill="#D7DADF" text-anchor="end">index 6</text>
<circle cx="348" cy="199.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="225.0" font-size="12" fill="#D7DADF" text-anchor="end">index 7</text>
<circle cx="348" cy="221.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="247.0" font-size="12" fill="#D7DADF" text-anchor="end">index 8</text>
<circle cx="348" cy="243.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="269.0" font-size="12" fill="#D7DADF" text-anchor="end">index 9</text>
<circle cx="348" cy="265.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="291.0" font-size="12" fill="#D7DADF" text-anchor="end">index 10</text>
<circle cx="348" cy="287.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="306" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="322" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="322" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="322" font-size="11.5" fill="#9AA0A8">index_count</text>
<text x="326" y="322" font-size="11.5" fill="#E6E8EB" text-anchor="end">4</text>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="237" viewBox="0 0 360 237" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Any Branch Switch (NH) node">
<rect x="0.5" y="0.5" width="359" height="236" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Any Branch Switch (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_AnySwitch · Logic</text>
<circle cx="12" cy="67.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">input</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">output 0</text>
<circle cx="348" cy="67.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">output 1</text>
<circle cx="348" cy="89.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">output 2</text>
<circle cx="348" cy="111.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="137.0" font-size="12" fill="#D7DADF" text-anchor="end">output 3</text>
<circle cx="348" cy="133.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="159.0" font-size="12" fill="#D7DADF" text-anchor="end">output 4</text>
<circle cx="348" cy="155.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="181.0" font-size="12" fill="#D7DADF" text-anchor="end">index</text>
<circle cx="348" cy="177.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="196" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="212" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="212" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="212" font-size="11.5" fill="#9AA0A8">index</text>
<text x="326" y="212" font-size="11.5" fill="#E6E8EB" text-anchor="end">0</text>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

+19
View File
@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="149" viewBox="0 0 360 149" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Any Switch (NH) node">
<rect x="0.5" y="0.5" width="359" height="148" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Any Switch (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_AnySwitchBoolean · Logic</text>
<circle cx="12" cy="67.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">input_true</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">result</text>
<circle cx="348" cy="67.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<circle cx="12" cy="89.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="93.0" font-size="12" fill="#D7DADF">input_false</text>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">condition</text>
<circle cx="348" cy="89.0" r="5" fill="#F48FB1" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="108" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="124" font-size="11.5" fill="#9AA0A8">condition</text>
<rect x="308" y="114" width="30" height="13" rx="6.5" fill="#4C8BF5"/>
<circle cx="331" cy="120.5" r="5" fill="#F5F6F7"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

+28
View File
@@ -0,0 +1,28 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="207" viewBox="0 0 360 207" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Batch Index (NH) node">
<rect x="0.5" y="0.5" width="359" height="206" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Batch Index (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_BatchIndex · Batch</text>
<circle cx="12" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">batch</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">result</text>
<circle cx="348" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">original_count</text>
<circle cx="348" cy="89.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="108" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="124" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="124" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="124" font-size="11.5" fill="#9AA0A8">index</text>
<text x="326" y="124" font-size="11.5" fill="#E6E8EB" text-anchor="end">0</text>
<rect x="12" y="137" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="153" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="153" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="153" font-size="11.5" fill="#9AA0A8">end_index <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="326" y="153" font-size="11.5" fill="#E6E8EB" text-anchor="end">-1</text>
<rect x="12" y="166" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="182" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="182" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="182" font-size="11.5" fill="#9AA0A8">step <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="326" y="182" font-size="11.5" fill="#E6E8EB" text-anchor="end">1</text>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

+22
View File
@@ -0,0 +1,22 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="171" viewBox="0 0 360 171" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Batch Merge (NH) node">
<rect x="0.5" y="0.5" width="359" height="170" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Batch Merge (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_BatchMerge · Batch</text>
<circle cx="12" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">batch_a</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">result</text>
<circle cx="348" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<circle cx="12" cy="89.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="93.0" font-size="12" fill="#D7DADF">batch_b <tspan fill="#7d838c" font-style="italic">(optional)</tspan></text>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">count</text>
<circle cx="348" cy="89.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<circle cx="12" cy="111.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="115.0" font-size="12" fill="#D7DADF">batch_c <tspan fill="#7d838c" font-style="italic">(optional)</tspan></text>
<rect x="12" y="130" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="146" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="146" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="146" font-size="11.5" fill="#9AA0A8">resize_mode <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="326" y="146" font-size="11.5" fill="#E6E8EB" text-anchor="end">stretch</text>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

+13
View File
@@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="127" viewBox="0 0 360 127" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Boolean Switch (NH) node">
<rect x="0.5" y="0.5" width="359" height="126" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Boolean Switch (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_BooleanSwitch · Utils</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">boolean</text>
<circle cx="348" cy="67.0" r="5" fill="#F48FB1" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="86" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="102" font-size="11.5" fill="#9AA0A8">boolean_switch</text>
<rect x="308" y="92" width="30" height="13" rx="6.5" fill="#4C8BF5"/>
<circle cx="331" cy="98.5" r="5" fill="#F5F6F7"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

+55
View File
@@ -0,0 +1,55 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="432" viewBox="0 0 360 432" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Load Clip Index (NH) node">
<rect x="0.5" y="0.5" width="359" height="431" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Load Clip Index (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_ClipIndex · Loaders</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">clip</text>
<circle cx="348" cy="67.0" r="5" fill="#FFD500" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">filename</text>
<circle cx="348" cy="89.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">index</text>
<circle cx="348" cy="111.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="130" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="146" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="146" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="146" font-size="11.5" fill="#9AA0A8">select_mode</text>
<text x="326" y="146" font-size="11.5" fill="#E6E8EB" text-anchor="end">index</text>
<rect x="12" y="159" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="175" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="175" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="175" font-size="11.5" fill="#9AA0A8">type</text>
<text x="326" y="175" font-size="11.5" fill="#E6E8EB" text-anchor="end">stable_diffusion</text>
<rect x="12" y="188" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="204" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="204" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="204" font-size="11.5" fill="#9AA0A8">index</text>
<text x="326" y="204" font-size="11.5" fill="#E6E8EB" text-anchor="end">1</text>
<rect x="12" y="217" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="233" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="233" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="233" font-size="11.5" fill="#9AA0A8">clip_count</text>
<text x="326" y="233" font-size="11.5" fill="#E6E8EB" text-anchor="end">5</text>
<rect x="12" y="246" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="262" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="262" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="262" font-size="11.5" fill="#9AA0A8">clip_1 <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="326" y="262" font-size="11.5" fill="#E6E8EB" text-anchor="end">(choose a file)</text>
<rect x="12" y="275" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="291" font-size="11.5" fill="#9AA0A8">enable_1 <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<rect x="308" y="281" width="30" height="13" rx="6.5" fill="#4a4d53"/>
<circle cx="315" cy="287.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="304" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="320" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="320" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="320" font-size="11.5" fill="#9AA0A8">clip_2 <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="326" y="320" font-size="11.5" fill="#E6E8EB" text-anchor="end">(choose a file)</text>
<rect x="12" y="333" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="349" font-size="11.5" fill="#9AA0A8">enable_2 <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<rect x="308" y="339" width="30" height="13" rx="6.5" fill="#4a4d53"/>
<circle cx="315" cy="345.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="362" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="180.0" y="378" font-size="11" fill="#9AA0A8" text-anchor="middle" font-style="italic">clip_3 … clip_64</text>
<rect x="12" y="391" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="180.0" y="407" font-size="11" fill="#9AA0A8" text-anchor="middle" font-style="italic">enable_3 … enable_64</text>
</svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

+27
View File
@@ -0,0 +1,27 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="200" viewBox="0 0 360 200" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Compare (NH) node">
<rect x="0.5" y="0.5" width="359" height="199" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Compare (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_Compare · Logic</text>
<circle cx="12" cy="67.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">a</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">result</text>
<circle cx="348" cy="67.0" r="5" fill="#F48FB1" stroke="#1b1c1f" stroke-width="1.5"/>
<circle cx="12" cy="89.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="93.0" font-size="12" fill="#D7DADF">b</text>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">a_passthrough</text>
<circle cx="348" cy="89.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">b_passthrough</text>
<circle cx="348" cy="111.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="130" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="146" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="146" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="146" font-size="11.5" fill="#9AA0A8">op</text>
<text x="326" y="146" font-size="11.5" fill="#E6E8EB" text-anchor="end">is</text>
<rect x="12" y="159" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="175" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="175" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="175" font-size="11.5" fill="#9AA0A8">type_cast <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="326" y="175" font-size="11.5" fill="#E6E8EB" text-anchor="end">auto</text>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

+34
View File
@@ -0,0 +1,34 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="280" viewBox="0 0 360 280" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Counter (NH) node">
<rect x="0.5" y="0.5" width="359" height="279" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Counter (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_Counter · Batch</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">current</text>
<circle cx="348" cy="67.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">is_done</text>
<circle cx="348" cy="89.0" r="5" fill="#F48FB1" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">progress</text>
<circle cx="348" cy="111.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="137.0" font-size="12" fill="#D7DADF" text-anchor="end">remaining</text>
<circle cx="348" cy="133.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="152" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="168" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="168" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="168" font-size="11.5" fill="#9AA0A8">start</text>
<text x="326" y="168" font-size="11.5" fill="#E6E8EB" text-anchor="end">0</text>
<rect x="12" y="181" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="197" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="197" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="197" font-size="11.5" fill="#9AA0A8">step</text>
<text x="326" y="197" font-size="11.5" fill="#E6E8EB" text-anchor="end">1</text>
<rect x="12" y="210" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="226" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="226" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="226" font-size="11.5" fill="#9AA0A8">max_value</text>
<text x="326" y="226" font-size="11.5" fill="#E6E8EB" text-anchor="end">9</text>
<rect x="12" y="239" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="255" font-size="11.5" fill="#9AA0A8">reset</text>
<rect x="308" y="245" width="30" height="13" rx="6.5" fill="#4a4d53"/>
<circle cx="315" cy="251.5" r="5" fill="#F5F6F7"/>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

@@ -0,0 +1,55 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="432" viewBox="0 0 360 432" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Load Diffusion Model Index (NH) node">
<rect x="0.5" y="0.5" width="359" height="431" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Load Diffusion Model Index (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_DiffusionModelIndex · Loaders</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">model</text>
<circle cx="348" cy="67.0" r="5" fill="#B39DDB" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">filename</text>
<circle cx="348" cy="89.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">index</text>
<circle cx="348" cy="111.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="130" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="146" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="146" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="146" font-size="11.5" fill="#9AA0A8">select_mode</text>
<text x="326" y="146" font-size="11.5" fill="#E6E8EB" text-anchor="end">index</text>
<rect x="12" y="159" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="175" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="175" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="175" font-size="11.5" fill="#9AA0A8">index</text>
<text x="326" y="175" font-size="11.5" fill="#E6E8EB" text-anchor="end">1</text>
<rect x="12" y="188" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="204" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="204" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="204" font-size="11.5" fill="#9AA0A8">model_count</text>
<text x="326" y="204" font-size="11.5" fill="#E6E8EB" text-anchor="end">5</text>
<rect x="12" y="217" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="233" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="233" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="233" font-size="11.5" fill="#9AA0A8">weight_dtype</text>
<text x="326" y="233" font-size="11.5" fill="#E6E8EB" text-anchor="end">default</text>
<rect x="12" y="246" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="262" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="262" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="262" font-size="11.5" fill="#9AA0A8">diffusion_model_1 <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="326" y="262" font-size="11.5" fill="#E6E8EB" text-anchor="end">—</text>
<rect x="12" y="275" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="291" font-size="11.5" fill="#9AA0A8">enable_1 <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<rect x="308" y="281" width="30" height="13" rx="6.5" fill="#4a4d53"/>
<circle cx="315" cy="287.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="304" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="320" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="320" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="320" font-size="11.5" fill="#9AA0A8">diffusion_model_2 <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="326" y="320" font-size="11.5" fill="#E6E8EB" text-anchor="end">—</text>
<rect x="12" y="333" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="349" font-size="11.5" fill="#9AA0A8">enable_2 <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<rect x="308" y="339" width="30" height="13" rx="6.5" fill="#4a4d53"/>
<circle cx="315" cy="345.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="362" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="180.0" y="378" font-size="11" fill="#9AA0A8" text-anchor="middle" font-style="italic">diffusion_model_3 … diffusion_model_64</text>
<rect x="12" y="391" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="180.0" y="407" font-size="11" fill="#9AA0A8" text-anchor="middle" font-style="italic">enable_3 … enable_64</text>
</svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

+43
View File
@@ -0,0 +1,43 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="316" viewBox="0 0 360 316" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Garment Segment (NH) node">
<rect x="0.5" y="0.5" width="359" height="315" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Garment Segment (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_GarmentSegment · Vision</text>
<circle cx="12" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">image</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">mask</text>
<circle cx="348" cy="67.0" r="5" fill="#81C784" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">cutout_rgba</text>
<circle cx="348" cy="89.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">mask_image</text>
<circle cx="348" cy="111.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="130" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="146" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="146" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="146" font-size="11.5" fill="#9AA0A8">part</text>
<text x="326" y="146" font-size="11.5" fill="#E6E8EB" text-anchor="end">upper_garment</text>
<rect x="12" y="159" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="175" font-size="11.5" fill="#9AA0A8">attach_parts</text>
<rect x="308" y="165" width="30" height="13" rx="6.5" fill="#4C8BF5"/>
<circle cx="331" cy="171.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="188" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="204" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="204" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="204" font-size="11.5" fill="#9AA0A8">mask_expand</text>
<text x="326" y="204" font-size="11.5" fill="#E6E8EB" text-anchor="end">0</text>
<rect x="12" y="217" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="233" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="233" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="233" font-size="11.5" fill="#9AA0A8">mask_blur</text>
<text x="326" y="233" font-size="11.5" fill="#E6E8EB" text-anchor="end">0</text>
<rect x="12" y="246" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="262" font-size="11.5" fill="#9AA0A8">fill_holes</text>
<rect x="308" y="252" width="30" height="13" rx="6.5" fill="#4a4d53"/>
<circle cx="315" cy="258.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="275" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="291" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="291" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="291" font-size="11.5" fill="#9AA0A8">device</text>
<text x="326" y="291" font-size="11.5" fill="#E6E8EB" text-anchor="end">cuda</text>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

+15
View File
@@ -0,0 +1,15 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="127" viewBox="0 0 360 127" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Gate Switch (NH) node">
<rect x="0.5" y="0.5" width="359" height="126" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Gate Switch (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_GateSwitch · Logic</text>
<circle cx="12" cy="67.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">input</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">output</text>
<circle cx="348" cy="67.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="86" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="102" font-size="11.5" fill="#9AA0A8">enabled</text>
<rect x="308" y="92" width="30" height="13" rx="6.5" fill="#4C8BF5"/>
<circle cx="331" cy="98.5" r="5" fill="#F5F6F7"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

+19
View File
@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="149" viewBox="0 0 360 149" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="If/Else (NH) node">
<rect x="0.5" y="0.5" width="359" height="148" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">If/Else (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_IfElse · Logic</text>
<circle cx="12" cy="67.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">if_true</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">result</text>
<circle cx="348" cy="67.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<circle cx="12" cy="89.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="93.0" font-size="12" fill="#D7DADF">if_false</text>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">condition_out</text>
<circle cx="348" cy="89.0" r="5" fill="#F48FB1" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="108" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="124" font-size="11.5" fill="#9AA0A8">condition</text>
<rect x="308" y="114" width="30" height="13" rx="6.5" fill="#4C8BF5"/>
<circle cx="331" cy="120.5" r="5" fill="#F5F6F7"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

+33
View File
@@ -0,0 +1,33 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="236" viewBox="0 0 360 236" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Image Compare (NH) node">
<rect x="0.5" y="0.5" width="359" height="235" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Image Compare (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_ImageCompare · Image</text>
<circle cx="12" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">image_a</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">image</text>
<circle cx="348" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<circle cx="12" cy="89.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="93.0" font-size="12" fill="#D7DADF">image_b</text>
<rect x="12" y="108" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="124" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="124" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="124" font-size="11.5" fill="#9AA0A8">compare_mode</text>
<text x="326" y="124" font-size="11.5" fill="#E6E8EB" text-anchor="end">side_by_side</text>
<rect x="12" y="137" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="153" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="153" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="153" font-size="11.5" fill="#9AA0A8">split_position</text>
<text x="326" y="153" font-size="11.5" fill="#E6E8EB" text-anchor="end">0.50</text>
<rect x="12" y="166" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="182" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="182" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="182" font-size="11.5" fill="#9AA0A8">overlay_opacity</text>
<text x="326" y="182" font-size="11.5" fill="#E6E8EB" text-anchor="end">0.50</text>
<rect x="12" y="195" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="211" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="211" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="211" font-size="11.5" fill="#9AA0A8">resize_method</text>
<text x="326" y="211" font-size="11.5" fill="#E6E8EB" text-anchor="end">lanczos</text>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

@@ -0,0 +1,60 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="425" viewBox="0 0 360 425" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Image Grid Composite (NH) node">
<rect x="0.5" y="0.5" width="359" height="424" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Image Grid Composite (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_ImageGridComposite · Image</text>
<circle cx="12" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">images</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">image</text>
<circle cx="348" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">slot_count</text>
<circle cx="348" cy="89.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">used_count</text>
<circle cx="348" cy="111.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="137.0" font-size="12" fill="#D7DADF" text-anchor="end">grid_info</text>
<circle cx="348" cy="133.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="152" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="168" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="168" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="168" font-size="11.5" fill="#9AA0A8">columns</text>
<text x="326" y="168" font-size="11.5" fill="#E6E8EB" text-anchor="end">2</text>
<rect x="12" y="181" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="197" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="197" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="197" font-size="11.5" fill="#9AA0A8">rows</text>
<text x="326" y="197" font-size="11.5" fill="#E6E8EB" text-anchor="end">2</text>
<rect x="12" y="210" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="226" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="226" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="226" font-size="11.5" fill="#9AA0A8">cell_size_mode</text>
<text x="326" y="226" font-size="11.5" fill="#E6E8EB" text-anchor="end">manual</text>
<rect x="12" y="239" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="255" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="255" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="255" font-size="11.5" fill="#9AA0A8">cell_width</text>
<text x="326" y="255" font-size="11.5" fill="#E6E8EB" text-anchor="end">512</text>
<rect x="12" y="268" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="284" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="284" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="284" font-size="11.5" fill="#9AA0A8">cell_height</text>
<text x="326" y="284" font-size="11.5" fill="#E6E8EB" text-anchor="end">512</text>
<rect x="12" y="297" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="313" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="313" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="313" font-size="11.5" fill="#9AA0A8">resize_mode</text>
<text x="326" y="313" font-size="11.5" fill="#E6E8EB" text-anchor="end">stretch</text>
<rect x="12" y="326" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="342" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="342" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="342" font-size="11.5" fill="#9AA0A8">gap_x</text>
<text x="326" y="342" font-size="11.5" fill="#E6E8EB" text-anchor="end">0</text>
<rect x="12" y="355" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="371" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="371" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="371" font-size="11.5" fill="#9AA0A8">gap_y</text>
<text x="326" y="371" font-size="11.5" fill="#E6E8EB" text-anchor="end">0</text>
<rect x="12" y="384" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="400" font-size="11.5" fill="#9AA0A8">background_color_hex</text>
<text x="338" y="400" font-size="11.5" fill="#E6E8EB" text-anchor="end">#000000</text>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

+70
View File
@@ -0,0 +1,70 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="526" viewBox="0 0 360 526" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Image Label (NH) node">
<rect x="0.5" y="0.5" width="359" height="525" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Image Label (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_ImageLabel · Image</text>
<circle cx="12" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">image</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">image</text>
<circle cx="348" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="86" width="336" height="46" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="102" font-size="11.5" fill="#9AA0A8">label_text</text>
<text x="22" y="122" font-size="11.5" fill="#E6E8EB" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH Label</text>
<rect x="12" y="137" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="153" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="153" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="153" font-size="11.5" fill="#9AA0A8">position</text>
<text x="326" y="153" font-size="11.5" fill="#E6E8EB" text-anchor="end">header</text>
<rect x="12" y="166" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="182" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="182" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="182" font-size="11.5" fill="#9AA0A8">frame_height</text>
<text x="326" y="182" font-size="11.5" fill="#E6E8EB" text-anchor="end">80</text>
<rect x="12" y="195" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="211" font-size="11.5" fill="#9AA0A8">fill_enabled</text>
<rect x="308" y="201" width="30" height="13" rx="6.5" fill="#4C8BF5"/>
<circle cx="331" cy="207.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="224" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="240" font-size="11.5" fill="#9AA0A8">frame_color_hex</text>
<text x="338" y="240" font-size="11.5" fill="#E6E8EB" text-anchor="end">#111111</text>
<rect x="12" y="253" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="269" font-size="11.5" fill="#9AA0A8">text_color_hex</text>
<text x="338" y="269" font-size="11.5" fill="#E6E8EB" text-anchor="end">#FFFFFF</text>
<rect x="12" y="282" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="298" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="298" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="298" font-size="11.5" fill="#9AA0A8">font_name</text>
<text x="326" y="298" font-size="11.5" fill="#E6E8EB" text-anchor="end">default</text>
<rect x="12" y="311" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="327" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="327" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="327" font-size="11.5" fill="#9AA0A8">font_size</text>
<text x="326" y="327" font-size="11.5" fill="#E6E8EB" text-anchor="end">32</text>
<rect x="12" y="340" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="356" font-size="11.5" fill="#9AA0A8">fit_text</text>
<rect x="308" y="346" width="30" height="13" rx="6.5" fill="#4C8BF5"/>
<circle cx="331" cy="352.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="369" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="385" font-size="11.5" fill="#9AA0A8">overlay_on_image</text>
<rect x="308" y="375" width="30" height="13" rx="6.5" fill="#4a4d53"/>
<circle cx="315" cy="381.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="398" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="414" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="414" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="414" font-size="11.5" fill="#9AA0A8">align</text>
<text x="326" y="414" font-size="11.5" fill="#E6E8EB" text-anchor="end">left</text>
<rect x="12" y="427" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="443" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="443" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="443" font-size="11.5" fill="#9AA0A8">padding_x</text>
<text x="326" y="443" font-size="11.5" fill="#E6E8EB" text-anchor="end">20</text>
<rect x="12" y="456" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="472" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="472" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="472" font-size="11.5" fill="#9AA0A8">outline_width</text>
<text x="326" y="472" font-size="11.5" fill="#E6E8EB" text-anchor="end">0</text>
<rect x="12" y="485" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="501" font-size="11.5" fill="#9AA0A8">outline_color_hex</text>
<text x="338" y="501" font-size="11.5" fill="#E6E8EB" text-anchor="end">#000000</text>
</svg>

After

Width:  |  Height:  |  Size: 5.3 KiB

@@ -0,0 +1,48 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="345" viewBox="0 0 360 345" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Image Resize Unit (NH) node">
<rect x="0.5" y="0.5" width="359" height="344" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Image Resize Unit (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_ImageResizeByUnit · Image</text>
<circle cx="12" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">image</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">image</text>
<circle cx="348" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">width_px</text>
<circle cx="348" cy="89.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">height_px</text>
<circle cx="348" cy="111.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="130" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="146" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="146" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="146" font-size="11.5" fill="#9AA0A8">unit</text>
<text x="326" y="146" font-size="11.5" fill="#E6E8EB" text-anchor="end">pixel</text>
<rect x="12" y="159" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="175" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="175" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="175" font-size="11.5" fill="#9AA0A8">width_value</text>
<text x="326" y="175" font-size="11.5" fill="#E6E8EB" text-anchor="end">1024.0</text>
<rect x="12" y="188" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="204" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="204" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="204" font-size="11.5" fill="#9AA0A8">height_value</text>
<text x="326" y="204" font-size="11.5" fill="#E6E8EB" text-anchor="end">1024.0</text>
<rect x="12" y="217" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="233" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="233" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="233" font-size="11.5" fill="#9AA0A8">dpi</text>
<text x="326" y="233" font-size="11.5" fill="#E6E8EB" text-anchor="end">300</text>
<rect x="12" y="246" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="262" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="262" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="262" font-size="11.5" fill="#9AA0A8">resize_mode</text>
<text x="326" y="262" font-size="11.5" fill="#E6E8EB" text-anchor="end">stretch</text>
<rect x="12" y="275" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="291" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="291" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="291" font-size="11.5" fill="#9AA0A8">resize_method</text>
<text x="326" y="291" font-size="11.5" fill="#E6E8EB" text-anchor="end">nearest</text>
<rect x="12" y="304" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="320" font-size="11.5" fill="#9AA0A8">pad_color_hex</text>
<text x="338" y="320" font-size="11.5" fill="#E6E8EB" text-anchor="end">#000000</text>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

+41
View File
@@ -0,0 +1,41 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="324" viewBox="0 0 360 324" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Image Tile (NH) node">
<rect x="0.5" y="0.5" width="359" height="323" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Image Tile (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_ImageTile · Image</text>
<circle cx="12" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">image</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">tiles</text>
<circle cx="348" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">tile_data</text>
<circle cx="348" cy="89.0" r="5" fill="#E0B272" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">tile_count</text>
<circle cx="348" cy="111.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="137.0" font-size="12" fill="#D7DADF" text-anchor="end">overlap_x</text>
<circle cx="348" cy="133.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="159.0" font-size="12" fill="#D7DADF" text-anchor="end">overlap_y</text>
<circle cx="348" cy="155.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="181.0" font-size="12" fill="#D7DADF" text-anchor="end">tile_info</text>
<circle cx="348" cy="177.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="196" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="212" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="212" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="212" font-size="11.5" fill="#9AA0A8">tile_mode</text>
<text x="326" y="212" font-size="11.5" fill="#E6E8EB" text-anchor="end">original_ratio</text>
<rect x="12" y="225" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="241" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="241" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="241" font-size="11.5" fill="#9AA0A8">tile_size</text>
<text x="326" y="241" font-size="11.5" fill="#E6E8EB" text-anchor="end">1024</text>
<rect x="12" y="254" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="270" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="270" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="270" font-size="11.5" fill="#9AA0A8">tile_width</text>
<text x="326" y="270" font-size="11.5" fill="#E6E8EB" text-anchor="end">1024</text>
<rect x="12" y="283" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="299" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="299" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="299" font-size="11.5" fill="#9AA0A8">tile_height</text>
<text x="326" y="299" font-size="11.5" fill="#E6E8EB" text-anchor="end">1024</text>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

+15
View File
@@ -0,0 +1,15 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="112" viewBox="0 0 360 112" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Image Untile (NH) node">
<rect x="0.5" y="0.5" width="359" height="111" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Image Untile (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_ImageUntile · Image</text>
<circle cx="12" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">tiles</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">image</text>
<circle cx="348" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<circle cx="12" cy="89.0" r="5" fill="#E0B272" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="93.0" font-size="12" fill="#D7DADF">tile_data</text>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">untile_info</text>
<circle cx="348" cy="89.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -0,0 +1,64 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="432" viewBox="0 0 360 432" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="NH Large Image Compare node">
<rect x="0.5" y="0.5" width="359" height="431" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#F6AD55"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">NH Large Image Compare</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_LargeImageCompare · Image</text>
<circle cx="12" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">image_a</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">image_a</text>
<circle cx="348" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<circle cx="12" cy="89.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="93.0" font-size="12" fill="#D7DADF">image_b</text>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">image_b</text>
<circle cx="348" cy="89.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">manifest</text>
<circle cx="348" cy="111.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="130" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="146" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="146" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="146" font-size="11.5" fill="#9AA0A8">compare_mode</text>
<text x="326" y="146" font-size="11.5" fill="#E6E8EB" text-anchor="end">slider</text>
<rect x="12" y="159" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="175" font-size="11.5" fill="#9AA0A8">sync_pan_zoom</text>
<rect x="308" y="165" width="30" height="13" rx="6.5" fill="#4C8BF5"/>
<circle cx="331" cy="171.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="188" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="204" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="204" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="204" font-size="11.5" fill="#9AA0A8">preview_megapixels</text>
<text x="326" y="204" font-size="11.5" fill="#E6E8EB" text-anchor="end">1.00</text>
<rect x="12" y="217" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="233" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="233" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="233" font-size="11.5" fill="#9AA0A8">preview_format</text>
<text x="326" y="233" font-size="11.5" fill="#E6E8EB" text-anchor="end">jpg</text>
<rect x="12" y="246" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="262" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="262" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="262" font-size="11.5" fill="#9AA0A8">preview_quality</text>
<text x="326" y="262" font-size="11.5" fill="#E6E8EB" text-anchor="end">90</text>
<rect x="12" y="275" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="291" font-size="11.5" fill="#9AA0A8">title</text>
<text x="338" y="291" font-size="11.5" fill="#E6E8EB" text-anchor="end">NH Large Image Compare</text>
<rect x="12" y="304" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="320" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="320" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="320" font-size="11.5" fill="#9AA0A8">tile_size <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="326" y="320" font-size="11.5" fill="#E6E8EB" text-anchor="end">512</text>
<rect x="12" y="333" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="349" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="349" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="349" font-size="11.5" fill="#9AA0A8">tile_format <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="326" y="349" font-size="11.5" fill="#E6E8EB" text-anchor="end">jpg</text>
<rect x="12" y="362" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="378" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="378" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="378" font-size="11.5" fill="#9AA0A8">tile_quality <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="326" y="378" font-size="11.5" fill="#E6E8EB" text-anchor="end">90</text>
<rect x="12" y="391" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="407" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="407" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="407" font-size="11.5" fill="#9AA0A8">cache_policy <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="326" y="407" font-size="11.5" fill="#E6E8EB" text-anchor="end">temp</text>
</svg>

After

Width:  |  Height:  |  Size: 5.1 KiB

@@ -0,0 +1,56 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="381" viewBox="0 0 360 381" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="NH Large Image Preview node">
<rect x="0.5" y="0.5" width="359" height="380" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#F6AD55"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">NH Large Image Preview</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_LargeImagePreview · Image</text>
<circle cx="12" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">image</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">image</text>
<circle cx="348" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">manifest</text>
<circle cx="348" cy="89.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="108" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="124" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="124" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="124" font-size="11.5" fill="#9AA0A8">preview_megapixels</text>
<text x="326" y="124" font-size="11.5" fill="#E6E8EB" text-anchor="end">1.00</text>
<rect x="12" y="137" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="153" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="153" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="153" font-size="11.5" fill="#9AA0A8">preview_format</text>
<text x="326" y="153" font-size="11.5" fill="#E6E8EB" text-anchor="end">jpg</text>
<rect x="12" y="166" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="182" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="182" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="182" font-size="11.5" fill="#9AA0A8">preview_quality</text>
<text x="326" y="182" font-size="11.5" fill="#E6E8EB" text-anchor="end">90</text>
<rect x="12" y="195" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="211" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="211" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="211" font-size="11.5" fill="#9AA0A8">viewer_mode</text>
<text x="326" y="211" font-size="11.5" fill="#E6E8EB" text-anchor="end">modal</text>
<rect x="12" y="224" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="240" font-size="11.5" fill="#9AA0A8">title</text>
<text x="338" y="240" font-size="11.5" fill="#E6E8EB" text-anchor="end">NH Large Image Preview</text>
<rect x="12" y="253" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="269" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="269" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="269" font-size="11.5" fill="#9AA0A8">tile_size <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="326" y="269" font-size="11.5" fill="#E6E8EB" text-anchor="end">512</text>
<rect x="12" y="282" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="298" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="298" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="298" font-size="11.5" fill="#9AA0A8">tile_format <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="326" y="298" font-size="11.5" fill="#E6E8EB" text-anchor="end">jpg</text>
<rect x="12" y="311" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="327" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="327" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="327" font-size="11.5" fill="#9AA0A8">tile_quality <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="326" y="327" font-size="11.5" fill="#E6E8EB" text-anchor="end">90</text>
<rect x="12" y="340" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="356" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="356" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="356" font-size="11.5" fill="#9AA0A8">cache_policy <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="326" y="356" font-size="11.5" fill="#E6E8EB" text-anchor="end">temp</text>
</svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

@@ -0,0 +1,33 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="280" viewBox="0 0 360 280" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="NH Large Preview Cache Manager node">
<rect x="0.5" y="0.5" width="359" height="279" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#F6AD55"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">NH Large Preview Cache Manager</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_LargePreviewCacheManager · Image</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">summary</text>
<circle cx="348" cy="67.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">removed</text>
<circle cx="348" cy="89.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">freed_mb</text>
<circle cx="348" cy="111.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="137.0" font-size="12" fill="#D7DADF" text-anchor="end">cache_root</text>
<circle cx="348" cy="133.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="152" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="168" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="168" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="168" font-size="11.5" fill="#9AA0A8">action</text>
<text x="326" y="168" font-size="11.5" fill="#E6E8EB" text-anchor="end">info</text>
<rect x="12" y="181" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="197" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="197" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="197" font-size="11.5" fill="#9AA0A8">max_age_hours</text>
<text x="326" y="197" font-size="11.5" fill="#E6E8EB" text-anchor="end">24.0</text>
<rect x="12" y="210" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="226" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="226" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="226" font-size="11.5" fill="#9AA0A8">max_total_mb</text>
<text x="326" y="226" font-size="11.5" fill="#E6E8EB" text-anchor="end">2048.0</text>
<rect x="12" y="239" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="255" font-size="11.5" fill="#9AA0A8">cache_id</text>
<text x="338" y="255" font-size="11.5" fill="#E6E8EB" text-anchor="end"></text>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

@@ -0,0 +1,90 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="621" viewBox="0 0 360 621" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Layer Layout Composite (NH) node">
<rect x="0.5" y="0.5" width="359" height="620" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Layer Layout Composite (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_LayerLayoutComposite · Image</text>
<circle cx="12" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">images</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">image</text>
<circle cx="348" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<circle cx="12" cy="89.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="93.0" font-size="12" fill="#D7DADF">background_image <tspan fill="#7d838c" font-style="italic">(optional)</tspan></text>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">slot_count</text>
<circle cx="348" cy="89.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">used_count</text>
<circle cx="348" cy="111.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="137.0" font-size="12" fill="#D7DADF" text-anchor="end">layout_info</text>
<circle cx="348" cy="133.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="152" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="168" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="168" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="168" font-size="11.5" fill="#9AA0A8">canvas_width</text>
<text x="326" y="168" font-size="11.5" fill="#E6E8EB" text-anchor="end">2480</text>
<rect x="12" y="181" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="197" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="197" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="197" font-size="11.5" fill="#9AA0A8">canvas_height</text>
<text x="326" y="197" font-size="11.5" fill="#E6E8EB" text-anchor="end">3508</text>
<rect x="12" y="210" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="226" font-size="11.5" fill="#9AA0A8">background_color</text>
<text x="338" y="226" font-size="11.5" fill="#E6E8EB" text-anchor="end">#FFFFFF</text>
<rect x="12" y="239" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="255" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="255" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="255" font-size="11.5" fill="#9AA0A8">layout_mode</text>
<text x="326" y="255" font-size="11.5" fill="#E6E8EB" text-anchor="end">preset</text>
<rect x="12" y="268" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="284" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="284" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="284" font-size="11.5" fill="#9AA0A8">layout_preset</text>
<text x="326" y="284" font-size="11.5" fill="#E6E8EB" text-anchor="end">2x3</text>
<rect x="12" y="297" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="313" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="313" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="313" font-size="11.5" fill="#9AA0A8">resize_mode</text>
<text x="326" y="313" font-size="11.5" fill="#E6E8EB" text-anchor="end">cover</text>
<rect x="12" y="326" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="342" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="342" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="342" font-size="11.5" fill="#9AA0A8">columns</text>
<text x="326" y="342" font-size="11.5" fill="#E6E8EB" text-anchor="end">2</text>
<rect x="12" y="355" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="371" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="371" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="371" font-size="11.5" fill="#9AA0A8">rows</text>
<text x="326" y="371" font-size="11.5" fill="#E6E8EB" text-anchor="end">3</text>
<rect x="12" y="384" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="400" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="400" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="400" font-size="11.5" fill="#9AA0A8">margin_x</text>
<text x="326" y="400" font-size="11.5" fill="#E6E8EB" text-anchor="end">150</text>
<rect x="12" y="413" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="429" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="429" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="429" font-size="11.5" fill="#9AA0A8">margin_y</text>
<text x="326" y="429" font-size="11.5" fill="#E6E8EB" text-anchor="end">150</text>
<rect x="12" y="442" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="458" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="458" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="458" font-size="11.5" fill="#9AA0A8">gap_x</text>
<text x="326" y="458" font-size="11.5" fill="#E6E8EB" text-anchor="end">150</text>
<rect x="12" y="471" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="487" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="487" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="487" font-size="11.5" fill="#9AA0A8">gap_y</text>
<text x="326" y="487" font-size="11.5" fill="#E6E8EB" text-anchor="end">150</text>
<rect x="12" y="500" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="516" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="516" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="516" font-size="11.5" fill="#9AA0A8">custom_units</text>
<text x="326" y="516" font-size="11.5" fill="#E6E8EB" text-anchor="end">auto</text>
<rect x="12" y="529" width="336" height="46" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="545" font-size="11.5" fill="#9AA0A8">custom_layout</text>
<text x="22" y="565" font-size="11.5" fill="#E6E8EB" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">0.06,0.04,0.39,0.20 ⏎ 0.55,0.…</text>
<rect x="12" y="580" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="596" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="596" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="596" font-size="11.5" fill="#9AA0A8">background_fit</text>
<text x="326" y="596" font-size="11.5" fill="#E6E8EB" text-anchor="end">stretch</text>
</svg>

After

Width:  |  Height:  |  Size: 6.9 KiB

@@ -0,0 +1,29 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="251" viewBox="0 0 360 251" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Layer Stack Composite (NH) node">
<rect x="0.5" y="0.5" width="359" height="250" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Layer Stack Composite (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_LayerStackComposite · Image</text>
<circle cx="12" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">background_image</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">image</text>
<circle cx="348" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<circle cx="12" cy="89.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="93.0" font-size="12" fill="#D7DADF">layer_1 <tspan fill="#7d838c" font-style="italic">(optional)</tspan></text>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">used_count</text>
<circle cx="348" cy="89.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<circle cx="12" cy="111.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="115.0" font-size="12" fill="#D7DADF">layer_2 <tspan fill="#7d838c" font-style="italic">(optional)</tspan></text>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">layer_info</text>
<circle cx="348" cy="111.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="130" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="146" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="146" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="146" font-size="11.5" fill="#9AA0A8">layer_count</text>
<text x="326" y="146" font-size="11.5" fill="#E6E8EB" text-anchor="end">1</text>
<rect x="12" y="159" width="336" height="46" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="175" font-size="11.5" fill="#9AA0A8">positions</text>
<text x="22" y="195" font-size="11.5" fill="#E6E8EB" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">0,0</text>
<rect x="12" y="210" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="180.0" y="226" font-size="11" fill="#9AA0A8" text-anchor="middle" font-style="italic">layer_3 … layer_64</text>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

+21
View File
@@ -0,0 +1,21 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="244" viewBox="0 0 360 244" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="List Create (NH) node">
<rect x="0.5" y="0.5" width="359" height="243" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">List Create (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_ListCreate · Batch</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">items</text>
<circle cx="348" cy="67.0" r="5" fill="#E0B272" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">count</text>
<circle cx="348" cy="89.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">first</text>
<circle cx="348" cy="111.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="137.0" font-size="12" fill="#D7DADF" text-anchor="end">last</text>
<circle cx="348" cy="133.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="152" width="336" height="46" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="168" font-size="11.5" fill="#9AA0A8">text</text>
<text x="22" y="188" font-size="11.5" fill="#E6E8EB" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">item 1 ⏎ item 2 ⏎ item 3</text>
<rect x="12" y="203" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="219" font-size="11.5" fill="#9AA0A8">delimiter <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="338" y="219" font-size="11.5" fill="#E6E8EB" text-anchor="end">\n</text>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

+25
View File
@@ -0,0 +1,25 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="222" viewBox="0 0 360 222" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="List Filter (NH) node">
<rect x="0.5" y="0.5" width="359" height="221" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">List Filter (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_ListFilter · Batch</text>
<circle cx="12" cy="67.0" r="5" fill="#E0B272" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">items</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">passed</text>
<circle cx="348" cy="67.0" r="5" fill="#E0B272" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">rejected</text>
<circle cx="348" cy="89.0" r="5" fill="#E0B272" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">passed_count</text>
<circle cx="348" cy="111.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="137.0" font-size="12" fill="#D7DADF" text-anchor="end">rejected_count</text>
<circle cx="348" cy="133.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="152" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="168" font-size="11.5" fill="#9AA0A8">condition</text>
<text x="338" y="168" font-size="11.5" fill="#E6E8EB" text-anchor="end">contains:dress</text>
<rect x="12" y="181" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="197" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="197" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="197" font-size="11.5" fill="#9AA0A8">mode</text>
<text x="326" y="197" font-size="11.5" fill="#E6E8EB" text-anchor="end">include</text>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="222" viewBox="0 0 360 222" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="List Index (NH) node">
<rect x="0.5" y="0.5" width="359" height="221" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">List Index (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_ListIndex · Batch</text>
<circle cx="12" cy="67.0" r="5" fill="#E0B272" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">items</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">item</text>
<circle cx="348" cy="67.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">is_first</text>
<circle cx="348" cy="89.0" r="5" fill="#F48FB1" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">is_last</text>
<circle cx="348" cy="111.0" r="5" fill="#F48FB1" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="137.0" font-size="12" fill="#D7DADF" text-anchor="end">count</text>
<circle cx="348" cy="133.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="152" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="168" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="168" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="168" font-size="11.5" fill="#9AA0A8">index</text>
<text x="326" y="168" font-size="11.5" fill="#E6E8EB" text-anchor="end">0</text>
<rect x="12" y="181" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="197" font-size="11.5" fill="#9AA0A8">wrap <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<rect x="308" y="187" width="30" height="13" rx="6.5" fill="#4C8BF5"/>
<circle cx="331" cy="193.5" r="5" fill="#F5F6F7"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

+28
View File
@@ -0,0 +1,28 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="251" viewBox="0 0 360 251" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Load Image Info (NH) node">
<rect x="0.5" y="0.5" width="359" height="250" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Load Image Info (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_LoadImageInfo · Image</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">image</text>
<circle cx="348" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">mask</text>
<circle cx="348" cy="89.0" r="5" fill="#81C784" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">file_name</text>
<circle cx="348" cy="111.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="137.0" font-size="12" fill="#D7DADF" text-anchor="end">path</text>
<circle cx="348" cy="133.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="152" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="168" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="168" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="168" font-size="11.5" fill="#9AA0A8">image</text>
<text x="326" y="168" font-size="11.5" fill="#E6E8EB" text-anchor="end">(choose a file)</text>
<rect x="12" y="181" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="197" font-size="11.5" fill="#9AA0A8">filename_extension</text>
<rect x="308" y="187" width="30" height="13" rx="6.5" fill="#4C8BF5"/>
<circle cx="331" cy="193.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="210" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="226" font-size="11.5" fill="#9AA0A8">path_include_filename</text>
<rect x="308" y="216" width="30" height="13" rx="6.5" fill="#4C8BF5"/>
<circle cx="331" cy="222.5" r="5" fill="#F5F6F7"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

@@ -0,0 +1,44 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="345" viewBox="0 0 360 345" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Load Images Folder (NH) node">
<rect x="0.5" y="0.5" width="359" height="344" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Load Images Folder (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_LoadImagesFromFolder · Image</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">images</text>
<circle cx="348" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">filename</text>
<circle cx="348" cy="89.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">path</text>
<circle cx="348" cy="111.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="130" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="146" font-size="11.5" fill="#9AA0A8">folder_path</text>
<text x="338" y="146" font-size="11.5" fill="#E6E8EB" text-anchor="end"></text>
<rect x="12" y="159" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="175" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="175" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="175" font-size="11.5" fill="#9AA0A8">index</text>
<text x="326" y="175" font-size="11.5" fill="#E6E8EB" text-anchor="end">0</text>
<rect x="12" y="188" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="204" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="204" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="204" font-size="11.5" fill="#9AA0A8">step</text>
<text x="326" y="204" font-size="11.5" fill="#E6E8EB" text-anchor="end">1</text>
<rect x="12" y="217" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="233" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="233" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="233" font-size="11.5" fill="#9AA0A8">seed_mode</text>
<text x="326" y="233" font-size="11.5" fill="#E6E8EB" text-anchor="end">fixed</text>
<rect x="12" y="246" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="262" font-size="11.5" fill="#9AA0A8">recursive</text>
<rect x="308" y="252" width="30" height="13" rx="6.5" fill="#4a4d53"/>
<circle cx="315" cy="258.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="275" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="291" font-size="11.5" fill="#9AA0A8">filename_extension</text>
<rect x="308" y="281" width="30" height="13" rx="6.5" fill="#4C8BF5"/>
<circle cx="331" cy="287.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="304" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="320" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="320" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="320" font-size="11.5" fill="#9AA0A8">path_output</text>
<text x="326" y="320" font-size="11.5" fill="#E6E8EB" text-anchor="end">file_path</text>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

@@ -0,0 +1,58 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="520" viewBox="0 0 360 520" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Load images matching node">
<rect x="0.5" y="0.5" width="359" height="519" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Load images matching</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_LoadImagesMatching · Image</text>
<circle cx="12" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">fallback_images <tspan fill="#7d838c" font-style="italic">(optional)</tspan></text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">images</text>
<circle cx="348" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">filename</text>
<circle cx="348" cy="89.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">path</text>
<circle cx="348" cy="111.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="137.0" font-size="12" fill="#D7DADF" text-anchor="end">matched_text</text>
<circle cx="348" cy="133.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="159.0" font-size="12" fill="#D7DADF" text-anchor="end">found</text>
<circle cx="348" cy="155.0" r="5" fill="#F48FB1" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="174" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="190" font-size="11.5" fill="#9AA0A8">source_folder_path</text>
<text x="338" y="190" font-size="11.5" fill="#E6E8EB" text-anchor="end"></text>
<rect x="12" y="203" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="219" font-size="11.5" fill="#9AA0A8">matching_root_path</text>
<text x="338" y="219" font-size="11.5" fill="#E6E8EB" text-anchor="end"></text>
<rect x="12" y="232" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="248" font-size="11.5" fill="#9AA0A8">match_text</text>
<text x="338" y="248" font-size="11.5" fill="#E6E8EB" text-anchor="end"></text>
<rect x="12" y="261" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="277" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="277" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="277" font-size="11.5" fill="#9AA0A8">match_mode</text>
<text x="326" y="277" font-size="11.5" fill="#E6E8EB" text-anchor="end">contains</text>
<rect x="12" y="290" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="306" font-size="11.5" fill="#9AA0A8">recursive</text>
<rect x="308" y="296" width="30" height="13" rx="6.5" fill="#4C8BF5"/>
<circle cx="331" cy="302.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="319" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="335" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="335" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="335" font-size="11.5" fill="#9AA0A8">index</text>
<text x="326" y="335" font-size="11.5" fill="#E6E8EB" text-anchor="end">0</text>
<rect x="12" y="348" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="364" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="364" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="364" font-size="11.5" fill="#9AA0A8">step</text>
<text x="326" y="364" font-size="11.5" fill="#E6E8EB" text-anchor="end">1</text>
<rect x="12" y="377" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="393" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="393" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="393" font-size="11.5" fill="#9AA0A8">repeat_count</text>
<text x="326" y="393" font-size="11.5" fill="#E6E8EB" text-anchor="end">1</text>
<rect x="12" y="406" width="336" height="46" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="422" font-size="11.5" fill="#9AA0A8">context_folder_names</text>
<text x="22" y="442" font-size="11.5" fill="#E6E8EB" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">Nam ⏎ Nu</text>
<rect x="12" y="457" width="336" height="46" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="473" font-size="11.5" fill="#9AA0A8">source_file_names <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="22" y="493" font-size="11.5" fill="#E6E8EB" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">…</text>
</svg>

After

Width:  |  Height:  |  Size: 4.8 KiB

+22
View File
@@ -0,0 +1,22 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="185" viewBox="0 0 360 185" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Logic Gate (NH) node">
<rect x="0.5" y="0.5" width="359" height="184" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Logic Gate (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_LogicGate · Logic</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">result</text>
<circle cx="348" cy="67.0" r="5" fill="#F48FB1" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="86" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="102" font-size="11.5" fill="#9AA0A8">a</text>
<rect x="308" y="92" width="30" height="13" rx="6.5" fill="#4a4d53"/>
<circle cx="315" cy="98.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="115" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="131" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="131" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="131" font-size="11.5" fill="#9AA0A8">op</text>
<text x="326" y="131" font-size="11.5" fill="#E6E8EB" text-anchor="end">AND</text>
<rect x="12" y="144" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="160" font-size="11.5" fill="#9AA0A8">b <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<rect x="308" y="150" width="30" height="13" rx="6.5" fill="#4a4d53"/>
<circle cx="315" cy="156.5" r="5" fill="#F5F6F7"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+66
View File
@@ -0,0 +1,66 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="483" viewBox="0 0 360 483" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Load LoRA Clip Index (NH) node">
<rect x="0.5" y="0.5" width="359" height="482" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Load LoRA Clip Index (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_LoraClipIndex · Loaders</text>
<circle cx="12" cy="67.0" r="5" fill="#B39DDB" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">model</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">model</text>
<circle cx="348" cy="67.0" r="5" fill="#B39DDB" stroke="#1b1c1f" stroke-width="1.5"/>
<circle cx="12" cy="89.0" r="5" fill="#FFD500" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="93.0" font-size="12" fill="#D7DADF">clip</text>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">clip</text>
<circle cx="348" cy="89.0" r="5" fill="#FFD500" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">filename</text>
<circle cx="348" cy="111.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="137.0" font-size="12" fill="#D7DADF" text-anchor="end">index</text>
<circle cx="348" cy="133.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="152" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="168" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="168" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="168" font-size="11.5" fill="#9AA0A8">select_mode</text>
<text x="326" y="168" font-size="11.5" fill="#E6E8EB" text-anchor="end">index</text>
<rect x="12" y="181" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="197" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="197" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="197" font-size="11.5" fill="#9AA0A8">index</text>
<text x="326" y="197" font-size="11.5" fill="#E6E8EB" text-anchor="end">1</text>
<rect x="12" y="210" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="226" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="226" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="226" font-size="11.5" fill="#9AA0A8">lora_count</text>
<text x="326" y="226" font-size="11.5" fill="#E6E8EB" text-anchor="end">5</text>
<rect x="12" y="239" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="255" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="255" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="255" font-size="11.5" fill="#9AA0A8">strength_model</text>
<text x="326" y="255" font-size="11.5" fill="#E6E8EB" text-anchor="end">1.00</text>
<rect x="12" y="268" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="284" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="284" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="284" font-size="11.5" fill="#9AA0A8">strength_clip</text>
<text x="326" y="284" font-size="11.5" fill="#E6E8EB" text-anchor="end">1.00</text>
<rect x="12" y="297" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="313" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="313" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="313" font-size="11.5" fill="#9AA0A8">lora_1 <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="326" y="313" font-size="11.5" fill="#E6E8EB" text-anchor="end">(choose a file)</text>
<rect x="12" y="326" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="342" font-size="11.5" fill="#9AA0A8">enable_1 <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<rect x="308" y="332" width="30" height="13" rx="6.5" fill="#4a4d53"/>
<circle cx="315" cy="338.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="355" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="371" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="371" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="371" font-size="11.5" fill="#9AA0A8">lora_2 <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="326" y="371" font-size="11.5" fill="#E6E8EB" text-anchor="end">(choose a file)</text>
<rect x="12" y="384" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="400" font-size="11.5" fill="#9AA0A8">enable_2 <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<rect x="308" y="390" width="30" height="13" rx="6.5" fill="#4a4d53"/>
<circle cx="315" cy="396.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="413" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="180.0" y="429" font-size="11" fill="#9AA0A8" text-anchor="middle" font-style="italic">lora_3 … lora_64</text>
<rect x="12" y="442" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="180.0" y="458" font-size="11" fill="#9AA0A8" text-anchor="middle" font-style="italic">enable_3 … enable_64</text>
</svg>

After

Width:  |  Height:  |  Size: 5.3 KiB

+57
View File
@@ -0,0 +1,57 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="432" viewBox="0 0 360 432" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Load LoRA Model Index (NH) node">
<rect x="0.5" y="0.5" width="359" height="431" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Load LoRA Model Index (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_LoraModelIndex · Loaders</text>
<circle cx="12" cy="67.0" r="5" fill="#B39DDB" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">model</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">model</text>
<circle cx="348" cy="67.0" r="5" fill="#B39DDB" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">filename</text>
<circle cx="348" cy="89.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">index</text>
<circle cx="348" cy="111.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="130" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="146" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="146" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="146" font-size="11.5" fill="#9AA0A8">select_mode</text>
<text x="326" y="146" font-size="11.5" fill="#E6E8EB" text-anchor="end">index</text>
<rect x="12" y="159" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="175" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="175" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="175" font-size="11.5" fill="#9AA0A8">index</text>
<text x="326" y="175" font-size="11.5" fill="#E6E8EB" text-anchor="end">1</text>
<rect x="12" y="188" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="204" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="204" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="204" font-size="11.5" fill="#9AA0A8">lora_count</text>
<text x="326" y="204" font-size="11.5" fill="#E6E8EB" text-anchor="end">5</text>
<rect x="12" y="217" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="233" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="233" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="233" font-size="11.5" fill="#9AA0A8">strength_model</text>
<text x="326" y="233" font-size="11.5" fill="#E6E8EB" text-anchor="end">1.00</text>
<rect x="12" y="246" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="262" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="262" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="262" font-size="11.5" fill="#9AA0A8">lora_1 <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="326" y="262" font-size="11.5" fill="#E6E8EB" text-anchor="end">(choose a file)</text>
<rect x="12" y="275" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="291" font-size="11.5" fill="#9AA0A8">enable_1 <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<rect x="308" y="281" width="30" height="13" rx="6.5" fill="#4a4d53"/>
<circle cx="315" cy="287.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="304" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="320" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="320" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="320" font-size="11.5" fill="#9AA0A8">lora_2 <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="326" y="320" font-size="11.5" fill="#E6E8EB" text-anchor="end">(choose a file)</text>
<rect x="12" y="333" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="349" font-size="11.5" fill="#9AA0A8">enable_2 <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<rect x="308" y="339" width="30" height="13" rx="6.5" fill="#4a4d53"/>
<circle cx="315" cy="345.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="362" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="180.0" y="378" font-size="11" fill="#9AA0A8" text-anchor="middle" font-style="italic">lora_3 … lora_64</text>
<rect x="12" y="391" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="180.0" y="407" font-size="11" fill="#9AA0A8" text-anchor="middle" font-style="italic">enable_3 … enable_64</text>
</svg>

After

Width:  |  Height:  |  Size: 4.7 KiB

@@ -0,0 +1,30 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="214" viewBox="0 0 360 214" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Mask Morphology (NH) node">
<rect x="0.5" y="0.5" width="359" height="213" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Mask Morphology (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_MaskAdvancedMorphology · Mask</text>
<circle cx="12" cy="67.0" r="5" fill="#81C784" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">mask</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">MASK</text>
<circle cx="348" cy="67.0" r="5" fill="#81C784" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="86" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="102" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="102" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="102" font-size="11.5" fill="#9AA0A8">horizontal_expand</text>
<text x="326" y="102" font-size="11.5" fill="#E6E8EB" text-anchor="end">0</text>
<rect x="12" y="115" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="131" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="131" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="131" font-size="11.5" fill="#9AA0A8">vertical_expand</text>
<text x="326" y="131" font-size="11.5" fill="#E6E8EB" text-anchor="end">0</text>
<rect x="12" y="144" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="160" font-size="11.5" fill="#9AA0A8">fill_holes</text>
<rect x="308" y="150" width="30" height="13" rx="6.5" fill="#4a4d53"/>
<circle cx="315" cy="156.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="173" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="189" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="189" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="189" font-size="11.5" fill="#9AA0A8">blur_radius</text>
<text x="326" y="189" font-size="11.5" fill="#E6E8EB" text-anchor="end">0.0</text>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

@@ -0,0 +1,18 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="149" viewBox="0 0 360 149" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="NH Mask Aspect Ratio Match node">
<rect x="0.5" y="0.5" width="359" height="148" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">NH Mask Aspect Ratio Match</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_MaskAspectRatioMatch · Mask</text>
<circle cx="12" cy="67.0" r="5" fill="#81C784" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">target_ratio_mask</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">MASK</text>
<circle cx="348" cy="67.0" r="5" fill="#81C784" stroke="#1b1c1f" stroke-width="1.5"/>
<circle cx="12" cy="89.0" r="5" fill="#81C784" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="93.0" font-size="12" fill="#D7DADF">mask_to_adjust</text>
<rect x="12" y="108" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="124" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="124" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="124" font-size="11.5" fill="#9AA0A8">mode</text>
<text x="326" y="124" font-size="11.5" fill="#E6E8EB" text-anchor="end">stretch</text>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

+23
View File
@@ -0,0 +1,23 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="222" viewBox="0 0 360 222" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Mask Properties (NH) node">
<rect x="0.5" y="0.5" width="359" height="221" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Mask Properties (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_MaskProperties · Mask</text>
<circle cx="12" cy="67.0" r="5" fill="#81C784" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">mask</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">width</text>
<circle cx="348" cy="67.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">height</text>
<circle cx="348" cy="89.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">x1</text>
<circle cx="348" cy="111.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="137.0" font-size="12" fill="#D7DADF" text-anchor="end">y1</text>
<circle cx="348" cy="133.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="159.0" font-size="12" fill="#D7DADF" text-anchor="end">x2</text>
<circle cx="348" cy="155.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="181.0" font-size="12" fill="#D7DADF" text-anchor="end">y2</text>
<circle cx="348" cy="177.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="203.0" font-size="12" fill="#D7DADF" text-anchor="end">bbox</text>
<circle cx="348" cy="199.0" r="5" fill="#E0B272" stroke="#1b1c1f" stroke-width="1.5"/>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

+33
View File
@@ -0,0 +1,33 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="236" viewBox="0 0 360 236" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="NH Mask-Aware Resize node">
<rect x="0.5" y="0.5" width="359" height="235" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">NH Mask-Aware Resize</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_MaskResizeImage · Image</text>
<circle cx="12" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">image</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">image</text>
<circle cx="348" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<circle cx="12" cy="89.0" r="5" fill="#81C784" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="93.0" font-size="12" fill="#D7DADF">mask</text>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">mask</text>
<circle cx="348" cy="89.0" r="5" fill="#81C784" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="108" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="124" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="124" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="124" font-size="11.5" fill="#9AA0A8">width</text>
<text x="326" y="124" font-size="11.5" fill="#E6E8EB" text-anchor="end">512</text>
<rect x="12" y="137" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="153" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="153" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="153" font-size="11.5" fill="#9AA0A8">height</text>
<text x="326" y="153" font-size="11.5" fill="#E6E8EB" text-anchor="end">512</text>
<rect x="12" y="166" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="182" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="182" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="182" font-size="11.5" fill="#9AA0A8">mode</text>
<text x="326" y="182" font-size="11.5" fill="#E6E8EB" text-anchor="end">crop</text>
<rect x="12" y="195" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="211" font-size="11.5" fill="#9AA0A8">pad_color_hex</text>
<text x="338" y="211" font-size="11.5" fill="#E6E8EB" text-anchor="end">#000000</text>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

+11
View File
@@ -0,0 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="90" viewBox="0 0 360 90" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="NH Create Box Mask node">
<rect x="0.5" y="0.5" width="359" height="89" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">NH Create Box Mask</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_MaskToBBox · Mask</text>
<circle cx="12" cy="67.0" r="5" fill="#81C784" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">mask</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">box_mask</text>
<circle cx="348" cy="67.0" r="5" fill="#81C784" stroke="#1b1c1f" stroke-width="1.5"/>
</svg>

After

Width:  |  Height:  |  Size: 1005 B

+41
View File
@@ -0,0 +1,41 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="316" viewBox="0 0 360 316" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Math Eval (NH) node">
<rect x="0.5" y="0.5" width="359" height="315" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Math Eval (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_MathEval · Logic</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">result_float</text>
<circle cx="348" cy="67.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">result_int</text>
<circle cx="348" cy="89.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">result_string</text>
<circle cx="348" cy="111.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="130" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="146" font-size="11.5" fill="#9AA0A8">expression</text>
<text x="338" y="146" font-size="11.5" fill="#E6E8EB" text-anchor="end">a + b</text>
<rect x="12" y="159" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="175" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="175" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="175" font-size="11.5" fill="#9AA0A8">a <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="326" y="175" font-size="11.5" fill="#E6E8EB" text-anchor="end">0.00</text>
<rect x="12" y="188" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="204" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="204" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="204" font-size="11.5" fill="#9AA0A8">b <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="326" y="204" font-size="11.5" fill="#E6E8EB" text-anchor="end">0.00</text>
<rect x="12" y="217" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="233" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="233" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="233" font-size="11.5" fill="#9AA0A8">c <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="326" y="233" font-size="11.5" fill="#E6E8EB" text-anchor="end">0.00</text>
<rect x="12" y="246" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="262" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="262" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="262" font-size="11.5" fill="#9AA0A8">d <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="326" y="262" font-size="11.5" fill="#E6E8EB" text-anchor="end">0.00</text>
<rect x="12" y="275" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="291" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="291" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="291" font-size="11.5" fill="#9AA0A8">round_to <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="326" y="291" font-size="11.5" fill="#E6E8EB" text-anchor="end">-1</text>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

+29
View File
@@ -0,0 +1,29 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="273" viewBox="0 0 360 273" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Multi-Slider (FLOAT) node">
<rect x="0.5" y="0.5" width="359" height="272" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Multi-Slider (FLOAT)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_MultiSliderFloat · Utils</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">Float 1</text>
<circle cx="348" cy="67.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">Float 2</text>
<circle cx="348" cy="89.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">Float 3</text>
<circle cx="348" cy="111.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="137.0" font-size="12" fill="#D7DADF" text-anchor="end">Float 4</text>
<circle cx="348" cy="133.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="159.0" font-size="12" fill="#D7DADF" text-anchor="end">Float 5</text>
<circle cx="348" cy="155.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="174" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="190" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="190" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="190" font-size="11.5" fill="#9AA0A8">float_1</text>
<text x="326" y="190" font-size="11.5" fill="#E6E8EB" text-anchor="end">0.00</text>
<rect x="12" y="203" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="219" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="219" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="219" font-size="11.5" fill="#9AA0A8">float_2</text>
<text x="326" y="219" font-size="11.5" fill="#E6E8EB" text-anchor="end">0.00</text>
<rect x="12" y="232" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="180.0" y="248" font-size="11" fill="#9AA0A8" text-anchor="middle" font-style="italic">float_3 … float_5</text>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

+29
View File
@@ -0,0 +1,29 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="273" viewBox="0 0 360 273" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Multi-Slider (INT) node">
<rect x="0.5" y="0.5" width="359" height="272" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Multi-Slider (INT)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_MultiSliderInt · Utils</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">Int 1</text>
<circle cx="348" cy="67.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">Int 2</text>
<circle cx="348" cy="89.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">Int 3</text>
<circle cx="348" cy="111.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="137.0" font-size="12" fill="#D7DADF" text-anchor="end">Int 4</text>
<circle cx="348" cy="133.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="159.0" font-size="12" fill="#D7DADF" text-anchor="end">Int 5</text>
<circle cx="348" cy="155.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="174" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="190" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="190" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="190" font-size="11.5" fill="#9AA0A8">int_1</text>
<text x="326" y="190" font-size="11.5" fill="#E6E8EB" text-anchor="end">0</text>
<rect x="12" y="203" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="219" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="219" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="219" font-size="11.5" fill="#9AA0A8">int_2</text>
<text x="326" y="219" font-size="11.5" fill="#E6E8EB" text-anchor="end">0</text>
<rect x="12" y="232" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="180.0" y="248" font-size="11" fill="#9AA0A8" text-anchor="middle" font-style="italic">int_3 … int_5</text>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

+17
View File
@@ -0,0 +1,17 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="171" viewBox="0 0 360 171" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Pack Universal (NH) node">
<rect x="0.5" y="0.5" width="359" height="170" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Pack Universal (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_PackUniversal · Utils/Pipe</text>
<circle cx="12" cy="67.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">input_0 <tspan fill="#7d838c" font-style="italic">(optional)</tspan></text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">package</text>
<circle cx="348" cy="67.0" r="5" fill="#E0B272" stroke="#1b1c1f" stroke-width="1.5"/>
<circle cx="12" cy="89.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="93.0" font-size="12" fill="#D7DADF">input_1 <tspan fill="#7d838c" font-style="italic">(optional)</tspan></text>
<circle cx="12" cy="111.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="115.0" font-size="12" fill="#D7DADF">input_2 <tspan fill="#7d838c" font-style="italic">(optional)</tspan></text>
<rect x="12" y="130" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="180.0" y="146" font-size="11" fill="#9AA0A8" text-anchor="middle" font-style="italic">input_3 … input_9</text>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

+70
View File
@@ -0,0 +1,70 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="519" viewBox="0 0 360 519" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Portrait Segment (NH) node">
<rect x="0.5" y="0.5" width="359" height="518" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Portrait Segment (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_PortraitSegment · Vision</text>
<circle cx="12" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">image</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">mask</text>
<circle cx="348" cy="67.0" r="5" fill="#81C784" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">cutout_rgba</text>
<circle cx="348" cy="89.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">mask_image</text>
<circle cx="348" cy="111.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="130" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="146" font-size="11.5" fill="#9AA0A8">face</text>
<rect x="308" y="136" width="30" height="13" rx="6.5" fill="#4C8BF5"/>
<circle cx="331" cy="142.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="159" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="175" font-size="11.5" fill="#9AA0A8">neck</text>
<rect x="308" y="165" width="30" height="13" rx="6.5" fill="#4C8BF5"/>
<circle cx="331" cy="171.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="188" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="204" font-size="11.5" fill="#9AA0A8">hair</text>
<rect x="308" y="194" width="30" height="13" rx="6.5" fill="#4C8BF5"/>
<circle cx="331" cy="200.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="217" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="233" font-size="11.5" fill="#9AA0A8">eyes</text>
<rect x="308" y="223" width="30" height="13" rx="6.5" fill="#4C8BF5"/>
<circle cx="331" cy="229.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="246" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="262" font-size="11.5" fill="#9AA0A8">lips</text>
<rect x="308" y="252" width="30" height="13" rx="6.5" fill="#4C8BF5"/>
<circle cx="331" cy="258.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="275" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="291" font-size="11.5" fill="#9AA0A8">nose</text>
<rect x="308" y="281" width="30" height="13" rx="6.5" fill="#4C8BF5"/>
<circle cx="331" cy="287.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="304" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="320" font-size="11.5" fill="#9AA0A8">eyebrows</text>
<rect x="308" y="310" width="30" height="13" rx="6.5" fill="#4C8BF5"/>
<circle cx="331" cy="316.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="333" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="349" font-size="11.5" fill="#9AA0A8">ears</text>
<rect x="308" y="339" width="30" height="13" rx="6.5" fill="#4C8BF5"/>
<circle cx="331" cy="345.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="362" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="378" font-size="11.5" fill="#9AA0A8">mouth_interior</text>
<rect x="308" y="368" width="30" height="13" rx="6.5" fill="#4a4d53"/>
<circle cx="315" cy="374.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="391" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="407" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="407" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="407" font-size="11.5" fill="#9AA0A8">mask_expand</text>
<text x="326" y="407" font-size="11.5" fill="#E6E8EB" text-anchor="end">0</text>
<rect x="12" y="420" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="436" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="436" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="436" font-size="11.5" fill="#9AA0A8">mask_blur</text>
<text x="326" y="436" font-size="11.5" fill="#E6E8EB" text-anchor="end">0</text>
<rect x="12" y="449" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="465" font-size="11.5" fill="#9AA0A8">fill_holes</text>
<rect x="308" y="455" width="30" height="13" rx="6.5" fill="#4C8BF5"/>
<circle cx="331" cy="461.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="478" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="494" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="494" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="494" font-size="11.5" fill="#9AA0A8">device</text>
<text x="326" y="494" font-size="11.5" fill="#E6E8EB" text-anchor="end">cuda</text>
</svg>

After

Width:  |  Height:  |  Size: 5.1 KiB

+24
View File
@@ -0,0 +1,24 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="207" viewBox="0 0 360 207" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Prompt Join (NH) node">
<rect x="0.5" y="0.5" width="359" height="206" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Prompt Join (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_PromptJoin · Text</text>
<circle cx="12" cy="67.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">text_1 <tspan fill="#7d838c" font-style="italic">(optional)</tspan></text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">result</text>
<circle cx="348" cy="67.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<circle cx="12" cy="89.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="93.0" font-size="12" fill="#D7DADF">text_2 <tspan fill="#7d838c" font-style="italic">(optional)</tspan></text>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">count</text>
<circle cx="348" cy="89.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="108" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="124" font-size="11.5" fill="#9AA0A8">separator</text>
<text x="338" y="124" font-size="11.5" fill="#E6E8EB" text-anchor="end">, </text>
<rect x="12" y="137" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="153" font-size="11.5" fill="#9AA0A8">skip_empty</text>
<rect x="308" y="143" width="30" height="13" rx="6.5" fill="#4C8BF5"/>
<circle cx="331" cy="149.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="166" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="180.0" y="182" font-size="11" fill="#9AA0A8" text-anchor="middle" font-style="italic">text_3 … text_5</text>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

+36
View File
@@ -0,0 +1,36 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="309" viewBox="0 0 360 309" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Prompt Scheduler (NH) node">
<rect x="0.5" y="0.5" width="359" height="308" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Prompt Scheduler (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_PromptScheduler · Text</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">current_prompt</text>
<circle cx="348" cy="67.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">progress</text>
<circle cx="348" cy="89.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">step_index</text>
<circle cx="348" cy="111.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="130" width="336" height="46" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="146" font-size="11.5" fill="#9AA0A8">prompts</text>
<text x="22" y="166" font-size="11.5" fill="#E6E8EB" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">prompt 1 ⏎ prompt 2 ⏎ prompt 3</text>
<rect x="12" y="181" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="197" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="197" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="197" font-size="11.5" fill="#9AA0A8">current_step</text>
<text x="326" y="197" font-size="11.5" fill="#E6E8EB" text-anchor="end">0</text>
<rect x="12" y="210" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="226" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="226" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="226" font-size="11.5" fill="#9AA0A8">total_steps</text>
<text x="326" y="226" font-size="11.5" fill="#E6E8EB" text-anchor="end">10</text>
<rect x="12" y="239" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="255" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="255" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="255" font-size="11.5" fill="#9AA0A8">mode</text>
<text x="326" y="255" font-size="11.5" fill="#E6E8EB" text-anchor="end">sequential</text>
<rect x="12" y="268" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="284" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="284" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="284" font-size="11.5" fill="#9AA0A8">seed <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="326" y="284" font-size="11.5" fill="#E6E8EB" text-anchor="end">0</text>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

+23
View File
@@ -0,0 +1,23 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="229" viewBox="0 0 360 229" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Prompt Template (NH) node">
<rect x="0.5" y="0.5" width="359" height="228" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Prompt Template (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_PromptTemplate · Text</text>
<circle cx="12" cy="67.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">var1 <tspan fill="#7d838c" font-style="italic">(optional)</tspan></text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">result</text>
<circle cx="348" cy="67.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<circle cx="12" cy="89.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="93.0" font-size="12" fill="#D7DADF">var2 <tspan fill="#7d838c" font-style="italic">(optional)</tspan></text>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">missing_vars</text>
<circle cx="348" cy="89.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="108" width="336" height="46" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="124" font-size="11.5" fill="#9AA0A8">template</text>
<text x="22" y="144" font-size="11.5" fill="#E6E8EB" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">a {color} {garment}, {style},…</text>
<rect x="12" y="159" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="175" font-size="11.5" fill="#9AA0A8">var_names</text>
<text x="338" y="175" font-size="11.5" fill="#E6E8EB" text-anchor="end">color,garment,style</text>
<rect x="12" y="188" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="180.0" y="204" font-size="11" fill="#9AA0A8" text-anchor="middle" font-style="italic">var3 … var6</text>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

+29
View File
@@ -0,0 +1,29 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="229" viewBox="0 0 360 229" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Random Choice (NH) node">
<rect x="0.5" y="0.5" width="359" height="228" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Random Choice (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_RandomChoice · Logic</text>
<circle cx="12" cy="67.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">input_0 <tspan fill="#7d838c" font-style="italic">(optional)</tspan></text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">result</text>
<circle cx="348" cy="67.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<circle cx="12" cy="89.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="93.0" font-size="12" fill="#D7DADF">input_1 <tspan fill="#7d838c" font-style="italic">(optional)</tspan></text>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">picked_index</text>
<circle cx="348" cy="89.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<circle cx="12" cy="111.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="115.0" font-size="12" fill="#D7DADF">input_2 <tspan fill="#7d838c" font-style="italic">(optional)</tspan></text>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">probabilities</text>
<circle cx="348" cy="111.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="130" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="146" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="146" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="146" font-size="11.5" fill="#9AA0A8">seed</text>
<text x="326" y="146" font-size="11.5" fill="#E6E8EB" text-anchor="end">0</text>
<rect x="12" y="159" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="175" font-size="11.5" fill="#9AA0A8">weights</text>
<text x="338" y="175" font-size="11.5" fill="#E6E8EB" text-anchor="end">1,1,1,1,1,1</text>
<rect x="12" y="188" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="180.0" y="204" font-size="11" fill="#9AA0A8" text-anchor="middle" font-style="italic">input_3 … input_5</text>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

@@ -0,0 +1,53 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="426" viewBox="0 0 360 426" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="NH Ratio Preset Image Resize node">
<rect x="0.5" y="0.5" width="359" height="425" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">NH Ratio Preset Image Resize</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_RatioPresetImageResize · Resolution</text>
<circle cx="12" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">image</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">image</text>
<circle cx="348" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">width</text>
<circle cx="348" cy="89.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">height</text>
<circle cx="348" cy="111.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="137.0" font-size="12" fill="#D7DADF" text-anchor="end">info</text>
<circle cx="348" cy="133.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="159.0" font-size="12" fill="#D7DADF" text-anchor="end">source_aspect_ratio</text>
<circle cx="348" cy="155.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="181.0" font-size="12" fill="#D7DADF" text-anchor="end">target_aspect_ratio</text>
<circle cx="348" cy="177.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="203.0" font-size="12" fill="#D7DADF" text-anchor="end">selected_preset</text>
<circle cx="348" cy="199.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="225.0" font-size="12" fill="#D7DADF" text-anchor="end">resize_data</text>
<circle cx="348" cy="221.0" r="5" fill="#E0B272" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="240" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="256" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="256" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="256" font-size="11.5" fill="#9AA0A8">model_family</text>
<text x="326" y="256" font-size="11.5" fill="#E6E8EB" text-anchor="end">FLUX.2 Klein 9B</text>
<rect x="12" y="269" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="285" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="285" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="285" font-size="11.5" fill="#9AA0A8">resolution_level</text>
<text x="326" y="285" font-size="11.5" fill="#E6E8EB" text-anchor="end">1 MP</text>
<rect x="12" y="298" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="314" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="314" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="314" font-size="11.5" fill="#9AA0A8">target_ratio</text>
<text x="326" y="314" font-size="11.5" fill="#E6E8EB" text-anchor="end">1:1</text>
<rect x="12" y="327" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="343" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="343" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="343" font-size="11.5" fill="#9AA0A8">resize_mode</text>
<text x="326" y="343" font-size="11.5" fill="#E6E8EB" text-anchor="end">crop</text>
<rect x="12" y="356" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="372" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="372" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="372" font-size="11.5" fill="#9AA0A8">resize_method</text>
<text x="326" y="372" font-size="11.5" fill="#E6E8EB" text-anchor="end">lanczos</text>
<rect x="12" y="385" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="401" font-size="11.5" fill="#9AA0A8">pad_color_hex</text>
<text x="338" y="401" font-size="11.5" fill="#E6E8EB" text-anchor="end">#FFFFFF</text>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

+29
View File
@@ -0,0 +1,29 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="302" viewBox="0 0 360 302" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Regex Extract (NH) node">
<rect x="0.5" y="0.5" width="359" height="301" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Regex Extract (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_RegexExtract · Text</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">result</text>
<circle cx="348" cy="67.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">matched</text>
<circle cx="348" cy="89.0" r="5" fill="#F48FB1" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">groups</text>
<circle cx="348" cy="111.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="137.0" font-size="12" fill="#D7DADF" text-anchor="end">count</text>
<circle cx="348" cy="133.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="152" width="336" height="46" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="168" font-size="11.5" fill="#9AA0A8">text</text>
<text x="22" y="188" font-size="11.5" fill="#E6E8EB" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">…</text>
<rect x="12" y="203" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="219" font-size="11.5" fill="#9AA0A8">pattern</text>
<text x="338" y="219" font-size="11.5" fill="#E6E8EB" text-anchor="end">(\d+)</text>
<rect x="12" y="232" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="248" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="248" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="248" font-size="11.5" fill="#9AA0A8">mode</text>
<text x="326" y="248" font-size="11.5" fill="#E6E8EB" text-anchor="end">match</text>
<rect x="12" y="261" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="277" font-size="11.5" fill="#9AA0A8">replacement <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="338" y="277" font-size="11.5" fill="#E6E8EB" text-anchor="end"></text>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

@@ -0,0 +1,29 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="222" viewBox="0 0 360 222" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="NH Remove Resize Padding node">
<rect x="0.5" y="0.5" width="359" height="221" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">NH Remove Resize Padding</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_RemoveResizePadding · Resolution</text>
<circle cx="12" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">image</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">image</text>
<circle cx="348" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<circle cx="12" cy="89.0" r="5" fill="#E0B272" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="93.0" font-size="12" fill="#D7DADF">resize_data</text>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">width</text>
<circle cx="348" cy="89.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">height</text>
<circle cx="348" cy="111.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="137.0" font-size="12" fill="#D7DADF" text-anchor="end">info</text>
<circle cx="348" cy="133.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="152" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="168" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="168" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="168" font-size="11.5" fill="#9AA0A8">output_size</text>
<text x="326" y="168" font-size="11.5" fill="#E6E8EB" text-anchor="end">content_region</text>
<rect x="12" y="181" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="197" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="197" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="197" font-size="11.5" fill="#9AA0A8">resize_method</text>
<text x="326" y="197" font-size="11.5" fill="#E6E8EB" text-anchor="end">lanczos</text>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

@@ -0,0 +1,22 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="171" viewBox="0 0 360 171" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="NH Restore Image Geometry node">
<rect x="0.5" y="0.5" width="359" height="170" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">NH Restore Image Geometry</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_RestoreImageGeometry · Resolution</text>
<circle cx="12" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">image</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">image</text>
<circle cx="348" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<circle cx="12" cy="89.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="93.0" font-size="12" fill="#D7DADF">original_image</text>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">mask</text>
<circle cx="348" cy="89.0" r="5" fill="#81C784" stroke="#1b1c1f" stroke-width="1.5"/>
<circle cx="12" cy="111.0" r="5" fill="#E0B272" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="115.0" font-size="12" fill="#D7DADF">resize_data</text>
<rect x="12" y="130" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="146" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="146" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="146" font-size="11.5" fill="#9AA0A8">resize_method</text>
<text x="326" y="146" font-size="11.5" fill="#E6E8EB" text-anchor="end">bicubic</text>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+42
View File
@@ -0,0 +1,42 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="338" viewBox="0 0 360 338" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="SAM3 Segment (NH) node">
<rect x="0.5" y="0.5" width="359" height="337" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">SAM3 Segment (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_SAM3Segment · Vision</text>
<circle cx="12" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">image</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">segs</text>
<circle cx="348" cy="67.0" r="5" fill="#E0B272" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">mask</text>
<circle cx="348" cy="89.0" r="5" fill="#81C784" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">count</text>
<circle cx="348" cy="111.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="130" width="336" height="46" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="146" font-size="11.5" fill="#9AA0A8">concepts</text>
<text x="22" y="166" font-size="11.5" fill="#E6E8EB" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">person</text>
<rect x="12" y="181" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="197" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="197" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="197" font-size="11.5" fill="#9AA0A8">threshold</text>
<text x="326" y="197" font-size="11.5" fill="#E6E8EB" text-anchor="end">0.50</text>
<rect x="12" y="210" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="226" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="226" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="226" font-size="11.5" fill="#9AA0A8">detail_level</text>
<text x="326" y="226" font-size="11.5" fill="#E6E8EB" text-anchor="end">balanced</text>
<rect x="12" y="239" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="255" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="255" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="255" font-size="11.5" fill="#9AA0A8">max_per_concept</text>
<text x="326" y="255" font-size="11.5" fill="#E6E8EB" text-anchor="end">0</text>
<rect x="12" y="268" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="284" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="284" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="284" font-size="11.5" fill="#9AA0A8">checkpoint</text>
<text x="326" y="284" font-size="11.5" fill="#E6E8EB" text-anchor="end">(choose a file)</text>
<rect x="12" y="297" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="313" font-size="11.5" fill="#9AA0A8">keep_model_loaded</text>
<rect x="308" y="303" width="30" height="13" rx="6.5" fill="#4a4d53"/>
<circle cx="315" cy="309.5" r="5" fill="#F5F6F7"/>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

+46
View File
@@ -0,0 +1,46 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="374" viewBox="0 0 360 374" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Save Image Path (NH) node">
<rect x="0.5" y="0.5" width="359" height="373" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#F6AD55"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Save Image Path (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_SaveImagePath · Image</text>
<circle cx="12" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">images</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">filenames</text>
<circle cx="348" cy="67.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">path</text>
<circle cx="348" cy="89.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">saved_count</text>
<circle cx="348" cy="111.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="130" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="146" font-size="11.5" fill="#9AA0A8">folder_path</text>
<text x="338" y="146" font-size="11.5" fill="#E6E8EB" text-anchor="end"></text>
<rect x="12" y="159" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="175" font-size="11.5" fill="#9AA0A8">create_new_folder</text>
<rect x="308" y="165" width="30" height="13" rx="6.5" fill="#4a4d53"/>
<circle cx="315" cy="171.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="188" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="204" font-size="11.5" fill="#9AA0A8">new_folder_name</text>
<text x="338" y="204" font-size="11.5" fill="#E6E8EB" text-anchor="end"></text>
<rect x="12" y="217" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="233" font-size="11.5" fill="#9AA0A8">filename_prefix</text>
<text x="338" y="233" font-size="11.5" fill="#E6E8EB" text-anchor="end">NH</text>
<rect x="12" y="246" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="262" font-size="11.5" fill="#9AA0A8">number_suffix</text>
<rect x="308" y="252" width="30" height="13" rx="6.5" fill="#4C8BF5"/>
<circle cx="331" cy="258.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="275" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="291" font-size="11.5" fill="#9AA0A8">preview_image</text>
<rect x="308" y="281" width="30" height="13" rx="6.5" fill="#4a4d53"/>
<circle cx="315" cy="287.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="304" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="320" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="320" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="320" font-size="11.5" fill="#9AA0A8">file_format</text>
<text x="326" y="320" font-size="11.5" fill="#E6E8EB" text-anchor="end">png</text>
<rect x="12" y="333" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="349" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="349" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="349" font-size="11.5" fill="#9AA0A8">dpi</text>
<text x="326" y="349" font-size="11.5" fill="#E6E8EB" text-anchor="end">300</text>
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

+32
View File
@@ -0,0 +1,32 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="258" viewBox="0 0 360 258" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="NH Save Large Image node">
<rect x="0.5" y="0.5" width="359" height="257" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#F6AD55"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">NH Save Large Image</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_SaveLargeImage · Image</text>
<circle cx="12" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">image</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">image</text>
<circle cx="348" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">file_path</text>
<circle cx="348" cy="89.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">saved_count</text>
<circle cx="348" cy="111.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="130" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="146" font-size="11.5" fill="#9AA0A8">filename_prefix</text>
<text x="338" y="146" font-size="11.5" fill="#E6E8EB" text-anchor="end">NH_Large</text>
<rect x="12" y="159" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="175" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="175" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="175" font-size="11.5" fill="#9AA0A8">file_format</text>
<text x="326" y="175" font-size="11.5" fill="#E6E8EB" text-anchor="end">png</text>
<rect x="12" y="188" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="204" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="204" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="204" font-size="11.5" fill="#9AA0A8">quality</text>
<text x="326" y="204" font-size="11.5" fill="#E6E8EB" text-anchor="end">95</text>
<rect x="12" y="217" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="233" font-size="11.5" fill="#9AA0A8">embed_metadata</text>
<rect x="308" y="223" width="30" height="13" rx="6.5" fill="#4C8BF5"/>
<circle cx="331" cy="229.5" r="5" fill="#F5F6F7"/>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

+14
View File
@@ -0,0 +1,14 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="149" viewBox="0 0 360 149" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Segs Compose (NH) node">
<rect x="0.5" y="0.5" width="359" height="148" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Segs Compose (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_SegsCompose · Vision</text>
<circle cx="12" cy="67.0" r="5" fill="#E0B272" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">segs</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">mask</text>
<circle cx="348" cy="67.0" r="5" fill="#81C784" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="86" width="336" height="46" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="102" font-size="11.5" fill="#9AA0A8">expression</text>
<text x="22" y="122" font-size="11.5" fill="#E6E8EB" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">all</text>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

+13
View File
@@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="112" viewBox="0 0 360 112" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Segs Preview (NH) node">
<rect x="0.5" y="0.5" width="359" height="111" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Segs Preview (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_SegsPreview · Vision</text>
<circle cx="12" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">image</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">preview</text>
<circle cx="348" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<circle cx="12" cy="89.0" r="5" fill="#E0B272" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="93.0" font-size="12" fill="#D7DADF">segs</text>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

+48
View File
@@ -0,0 +1,48 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="345" viewBox="0 0 360 345" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Segs Select (NH) node">
<rect x="0.5" y="0.5" width="359" height="344" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Segs Select (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_SegsSelect · Vision</text>
<circle cx="12" cy="67.0" r="5" fill="#E0B272" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">segs</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">segs</text>
<circle cx="348" cy="67.0" r="5" fill="#E0B272" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">mask</text>
<circle cx="348" cy="89.0" r="5" fill="#81C784" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">count</text>
<circle cx="348" cy="111.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="130" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="146" font-size="11.5" fill="#9AA0A8">labels</text>
<text x="338" y="146" font-size="11.5" fill="#E6E8EB" text-anchor="end"></text>
<rect x="12" y="159" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="175" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="175" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="175" font-size="11.5" fill="#9AA0A8">min_score</text>
<text x="326" y="175" font-size="11.5" fill="#E6E8EB" text-anchor="end">0.00</text>
<rect x="12" y="188" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="204" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="204" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="204" font-size="11.5" fill="#9AA0A8">min_area</text>
<text x="326" y="204" font-size="11.5" fill="#E6E8EB" text-anchor="end">0.0000</text>
<rect x="12" y="217" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="233" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="233" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="233" font-size="11.5" fill="#9AA0A8">max_area</text>
<text x="326" y="233" font-size="11.5" fill="#E6E8EB" text-anchor="end">1.0000</text>
<rect x="12" y="246" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="262" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="262" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="262" font-size="11.5" fill="#9AA0A8">sort_by</text>
<text x="326" y="262" font-size="11.5" fill="#E6E8EB" text-anchor="end">score</text>
<rect x="12" y="275" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="291" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="291" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="291" font-size="11.5" fill="#9AA0A8">start</text>
<text x="326" y="291" font-size="11.5" fill="#E6E8EB" text-anchor="end">0</text>
<rect x="12" y="304" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="320" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="320" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="320" font-size="11.5" fill="#9AA0A8">count</text>
<text x="326" y="320" font-size="11.5" fill="#E6E8EB" text-anchor="end">0</text>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

+21
View File
@@ -0,0 +1,21 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="156" viewBox="0 0 360 156" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Segs To Impact SEGS (NH) node">
<rect x="0.5" y="0.5" width="359" height="155" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Segs To Impact SEGS (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_SegsToImpactSEGS · Vision</text>
<circle cx="12" cy="67.0" r="5" fill="#E0B272" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">segs</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">segs</text>
<circle cx="348" cy="67.0" r="5" fill="#E0B272" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="86" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="102" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="102" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="102" font-size="11.5" fill="#9AA0A8">crop_factor</text>
<text x="326" y="102" font-size="11.5" fill="#E6E8EB" text-anchor="end">3.0</text>
<rect x="12" y="115" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="131" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="131" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="131" font-size="11.5" fill="#9AA0A8">batch_index</text>
<text x="326" y="131" font-size="11.5" fill="#E6E8EB" text-anchor="end">0</text>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

+17
View File
@@ -0,0 +1,17 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="156" viewBox="0 0 360 156" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Segs To Masks (NH) node">
<rect x="0.5" y="0.5" width="359" height="155" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Segs To Masks (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_SegsToMasks · Vision</text>
<circle cx="12" cy="67.0" r="5" fill="#E0B272" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">segs</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">masks</text>
<circle cx="348" cy="67.0" r="5" fill="#81C784" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">union</text>
<circle cx="348" cy="89.0" r="5" fill="#81C784" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">labels</text>
<circle cx="348" cy="111.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="137.0" font-size="12" fill="#D7DADF" text-anchor="end">count</text>
<circle cx="348" cy="133.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

+22
View File
@@ -0,0 +1,22 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="193" viewBox="0 0 360 193" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="NH Simple Face Paste node">
<rect x="0.5" y="0.5" width="359" height="192" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">NH Simple Face Paste</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_SimpleFacePaste · Image</text>
<circle cx="12" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">dest_image</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">IMAGE</text>
<circle cx="348" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<circle cx="12" cy="89.0" r="5" fill="#81C784" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="93.0" font-size="12" fill="#D7DADF">dest_mask</text>
<circle cx="12" cy="111.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="115.0" font-size="12" fill="#D7DADF">source_image</text>
<circle cx="12" cy="133.0" r="5" fill="#81C784" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="137.0" font-size="12" fill="#D7DADF">source_mask</text>
<rect x="12" y="152" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="168" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="168" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="168" font-size="11.5" fill="#9AA0A8">feathering</text>
<text x="326" y="168" font-size="11.5" fill="#E6E8EB" text-anchor="end">20</text>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

@@ -0,0 +1,48 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="397" viewBox="0 0 360 397" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="NH Smart Ratio Image Resize node">
<rect x="0.5" y="0.5" width="359" height="396" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">NH Smart Ratio Image Resize</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_SmartRatioImageResize · Resolution</text>
<circle cx="12" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">image</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">image</text>
<circle cx="348" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">width</text>
<circle cx="348" cy="89.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">height</text>
<circle cx="348" cy="111.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="137.0" font-size="12" fill="#D7DADF" text-anchor="end">info</text>
<circle cx="348" cy="133.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="159.0" font-size="12" fill="#D7DADF" text-anchor="end">source_aspect_ratio</text>
<circle cx="348" cy="155.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="181.0" font-size="12" fill="#D7DADF" text-anchor="end">target_aspect_ratio</text>
<circle cx="348" cy="177.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="203.0" font-size="12" fill="#D7DADF" text-anchor="end">selected_preset</text>
<circle cx="348" cy="199.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="225.0" font-size="12" fill="#D7DADF" text-anchor="end">resize_data</text>
<circle cx="348" cy="221.0" r="5" fill="#E0B272" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="240" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="256" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="256" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="256" font-size="11.5" fill="#9AA0A8">model_family</text>
<text x="326" y="256" font-size="11.5" fill="#E6E8EB" text-anchor="end">FLUX.2 Klein 9B</text>
<rect x="12" y="269" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="285" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="285" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="285" font-size="11.5" fill="#9AA0A8">resolution_level</text>
<text x="326" y="285" font-size="11.5" fill="#E6E8EB" text-anchor="end">1 MP</text>
<rect x="12" y="298" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="314" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="314" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="314" font-size="11.5" fill="#9AA0A8">resize_mode</text>
<text x="326" y="314" font-size="11.5" fill="#E6E8EB" text-anchor="end">cover_crop</text>
<rect x="12" y="327" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="343" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="343" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="343" font-size="11.5" fill="#9AA0A8">resize_method</text>
<text x="326" y="343" font-size="11.5" fill="#E6E8EB" text-anchor="end">lanczos</text>
<rect x="12" y="356" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="372" font-size="11.5" fill="#9AA0A8">pad_color_hex</text>
<text x="338" y="372" font-size="11.5" fill="#E6E8EB" text-anchor="end">#FFFFFF</text>
</svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

@@ -0,0 +1,50 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="389" viewBox="0 0 360 389" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="NH Smart Resolution Picker node">
<rect x="0.5" y="0.5" width="359" height="388" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">NH Smart Resolution Picker</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_SmartResolutionPicker · Resolution</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">width</text>
<circle cx="348" cy="67.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">height</text>
<circle cx="348" cy="89.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">latent</text>
<circle cx="348" cy="111.0" r="5" fill="#FF9CF9" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="137.0" font-size="12" fill="#D7DADF" text-anchor="end">info</text>
<circle cx="348" cy="133.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="159.0" font-size="12" fill="#D7DADF" text-anchor="end">aspect_ratio</text>
<circle cx="348" cy="155.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="174" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="190" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="190" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="190" font-size="11.5" fill="#9AA0A8">model_family</text>
<text x="326" y="190" font-size="11.5" fill="#E6E8EB" text-anchor="end">FLUX.2 Klein 9B</text>
<rect x="12" y="203" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="219" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="219" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="219" font-size="11.5" fill="#9AA0A8">preset</text>
<text x="326" y="219" font-size="11.5" fill="#E6E8EB" text-anchor="end">2 MP (sweet spot) ⭐ | 1…</text>
<rect x="12" y="232" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="248" font-size="11.5" fill="#9AA0A8">use_custom <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<rect x="308" y="238" width="30" height="13" rx="6.5" fill="#4a4d53"/>
<circle cx="315" cy="244.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="261" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="277" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="277" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="277" font-size="11.5" fill="#9AA0A8">custom_width <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="326" y="277" font-size="11.5" fill="#E6E8EB" text-anchor="end">1024</text>
<rect x="12" y="290" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="306" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="306" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="306" font-size="11.5" fill="#9AA0A8">custom_height <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="326" y="306" font-size="11.5" fill="#E6E8EB" text-anchor="end">1024</text>
<rect x="12" y="319" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="335" font-size="11.5" fill="#9AA0A8">swap_orientation <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<rect x="308" y="325" width="30" height="13" rx="6.5" fill="#4a4d53"/>
<circle cx="315" cy="331.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="348" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="364" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="364" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="364" font-size="11.5" fill="#9AA0A8">batch_size <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="326" y="364" font-size="11.5" fill="#E6E8EB" text-anchor="end">1</text>
</svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

+27
View File
@@ -0,0 +1,27 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="280" viewBox="0 0 360 280" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="String Operations (NH) node">
<rect x="0.5" y="0.5" width="359" height="279" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">String Operations (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_StringOps · Text</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">text_out</text>
<circle cx="348" cy="67.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">bool_out</text>
<circle cx="348" cy="89.0" r="5" fill="#F48FB1" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">int_out</text>
<circle cx="348" cy="111.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="130" width="336" height="46" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="146" font-size="11.5" fill="#9AA0A8">text</text>
<text x="22" y="166" font-size="11.5" fill="#E6E8EB" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">…</text>
<rect x="12" y="181" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="197" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="197" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="197" font-size="11.5" fill="#9AA0A8">operation</text>
<text x="326" y="197" font-size="11.5" fill="#E6E8EB" text-anchor="end">upper</text>
<rect x="12" y="210" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="226" font-size="11.5" fill="#9AA0A8">param_a <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="338" y="226" font-size="11.5" fill="#E6E8EB" text-anchor="end"></text>
<rect x="12" y="239" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="255" font-size="11.5" fill="#9AA0A8">param_b <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="338" y="255" font-size="11.5" fill="#E6E8EB" text-anchor="end"></text>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

+24
View File
@@ -0,0 +1,24 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="200" viewBox="0 0 360 200" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Switch N (NH) node">
<rect x="0.5" y="0.5" width="359" height="199" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Switch N (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_SwitchN · Logic</text>
<circle cx="12" cy="67.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">input_0 <tspan fill="#7d838c" font-style="italic">(optional)</tspan></text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">result</text>
<circle cx="348" cy="67.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<circle cx="12" cy="89.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="93.0" font-size="12" fill="#D7DADF">input_1 <tspan fill="#7d838c" font-style="italic">(optional)</tspan></text>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">count</text>
<circle cx="348" cy="89.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<circle cx="12" cy="111.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="115.0" font-size="12" fill="#D7DADF">input_2 <tspan fill="#7d838c" font-style="italic">(optional)</tspan></text>
<rect x="12" y="130" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="146" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="146" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="146" font-size="11.5" fill="#9AA0A8">index</text>
<text x="326" y="146" font-size="11.5" fill="#E6E8EB" text-anchor="end">0</text>
<rect x="12" y="159" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="180.0" y="175" font-size="11" fill="#9AA0A8" text-anchor="middle" font-style="italic">input_3 … input_9</text>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

+29
View File
@@ -0,0 +1,29 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="272" viewBox="0 0 360 272" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Text Concatenate (NH) node">
<rect x="0.5" y="0.5" width="359" height="271" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Text Concatenate (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_TextConcatenate · Text</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">text</text>
<circle cx="348" cy="67.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="86" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="102" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="102" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="102" font-size="11.5" fill="#9AA0A8">string_count</text>
<text x="326" y="102" font-size="11.5" fill="#E6E8EB" text-anchor="end">2</text>
<rect x="12" y="115" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="131" font-size="11.5" fill="#9AA0A8">delimiter</text>
<text x="338" y="131" font-size="11.5" fill="#E6E8EB" text-anchor="end">/</text>
<rect x="12" y="144" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="160" font-size="11.5" fill="#9AA0A8">clean_whitespace</text>
<rect x="308" y="150" width="30" height="13" rx="6.5" fill="#4C8BF5"/>
<circle cx="331" cy="156.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="173" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="189" font-size="11.5" fill="#9AA0A8">string_1 <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="338" y="189" font-size="11.5" fill="#E6E8EB" text-anchor="end"></text>
<rect x="12" y="202" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="218" font-size="11.5" fill="#9AA0A8">string_2 <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="338" y="218" font-size="11.5" fill="#E6E8EB" text-anchor="end"></text>
<rect x="12" y="231" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="180.0" y="247" font-size="11" fill="#9AA0A8" text-anchor="middle" font-style="italic">string_3 … string_64</text>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

+27
View File
@@ -0,0 +1,27 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="287" viewBox="0 0 360 287" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Text Index (NH) node">
<rect x="0.5" y="0.5" width="359" height="286" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Text Index (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_TextIndex · Text</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">text</text>
<circle cx="348" cy="67.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="86" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="102" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="102" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="102" font-size="11.5" fill="#9AA0A8">index</text>
<text x="326" y="102" font-size="11.5" fill="#E6E8EB" text-anchor="end">1</text>
<rect x="12" y="115" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="131" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="131" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="131" font-size="11.5" fill="#9AA0A8">text_count</text>
<text x="326" y="131" font-size="11.5" fill="#E6E8EB" text-anchor="end">5</text>
<rect x="12" y="144" width="336" height="46" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="160" font-size="11.5" fill="#9AA0A8">text_1 <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="22" y="180" font-size="11.5" fill="#E6E8EB" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">…</text>
<rect x="12" y="195" width="336" height="46" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="211" font-size="11.5" fill="#9AA0A8">text_2 <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="22" y="231" font-size="11.5" fill="#E6E8EB" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">…</text>
<rect x="12" y="246" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="180.0" y="262" font-size="11" fill="#9AA0A8" text-anchor="middle" font-style="italic">text_3 … text_64</text>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

+21
View File
@@ -0,0 +1,21 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="222" viewBox="0 0 360 222" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Text Random Line (NH) node">
<rect x="0.5" y="0.5" width="359" height="221" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Text Random Line (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_TextRandomLine · Text</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">text</text>
<circle cx="348" cy="67.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">picked_index</text>
<circle cx="348" cy="89.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">total_lines</text>
<circle cx="348" cy="111.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="130" width="336" height="46" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="146" font-size="11.5" fill="#9AA0A8">text</text>
<text x="22" y="166" font-size="11.5" fill="#E6E8EB" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">option A ⏎ option B ⏎ option C</text>
<rect x="12" y="181" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="197" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="197" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="197" font-size="11.5" fill="#9AA0A8">seed</text>
<text x="326" y="197" font-size="11.5" fill="#E6E8EB" text-anchor="end">0</text>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="273" viewBox="0 0 360 273" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Text Split (NH) node">
<rect x="0.5" y="0.5" width="359" height="272" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Text Split (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_TextSplit · Text</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">items_joined</text>
<circle cx="348" cy="67.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">count</text>
<circle cx="348" cy="89.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">first</text>
<circle cx="348" cy="111.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="137.0" font-size="12" fill="#D7DADF" text-anchor="end">last</text>
<circle cx="348" cy="133.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="152" width="336" height="46" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="168" font-size="11.5" fill="#9AA0A8">text</text>
<text x="22" y="188" font-size="11.5" fill="#E6E8EB" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">…</text>
<rect x="12" y="203" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="219" font-size="11.5" fill="#9AA0A8">delimiter</text>
<text x="338" y="219" font-size="11.5" fill="#E6E8EB" text-anchor="end">, </text>
<rect x="12" y="232" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="248" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="248" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="248" font-size="11.5" fill="#9AA0A8">max_splits <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="326" y="248" font-size="11.5" fill="#E6E8EB" text-anchor="end">-1</text>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

+52
View File
@@ -0,0 +1,52 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="462" viewBox="0 0 360 462" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Text Split Lines (NH) node">
<rect x="0.5" y="0.5" width="359" height="461" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Text Split Lines (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_TextSplitLines · Text</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">lines ≡</text>
<circle cx="348" cy="67.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">selected</text>
<circle cx="348" cy="89.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">count</text>
<circle cx="348" cy="111.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="137.0" font-size="12" fill="#D7DADF" text-anchor="end">preview</text>
<circle cx="348" cy="133.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="159.0" font-size="12" fill="#D7DADF" text-anchor="end">is_first</text>
<circle cx="348" cy="155.0" r="5" fill="#F48FB1" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="181.0" font-size="12" fill="#D7DADF" text-anchor="end">is_last</text>
<circle cx="348" cy="177.0" r="5" fill="#F48FB1" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="196" width="336" height="46" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="212" font-size="11.5" fill="#9AA0A8">text</text>
<text x="22" y="232" font-size="11.5" fill="#E6E8EB" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">first prompt ⏎ second prompt …</text>
<rect x="12" y="247" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="263" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="263" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="263" font-size="11.5" fill="#9AA0A8">index</text>
<text x="326" y="263" font-size="11.5" fill="#E6E8EB" text-anchor="end">0</text>
<rect x="12" y="276" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="292" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="292" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="292" font-size="11.5" fill="#9AA0A8">mode</text>
<text x="326" y="292" font-size="11.5" fill="#E6E8EB" text-anchor="end">sequential</text>
<rect x="12" y="305" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="321" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="321" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="321" font-size="11.5" fill="#9AA0A8">seed</text>
<text x="326" y="321" font-size="11.5" fill="#E6E8EB" text-anchor="end">0</text>
<rect x="12" y="334" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="350" font-size="11.5" fill="#9AA0A8">delimiter <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="338" y="350" font-size="11.5" fill="#E6E8EB" text-anchor="end">\n</text>
<rect x="12" y="363" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="379" font-size="11.5" fill="#9AA0A8">skip_empty <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<rect x="308" y="369" width="30" height="13" rx="6.5" fill="#4C8BF5"/>
<circle cx="331" cy="375.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="392" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="408" font-size="11.5" fill="#9AA0A8">strip_whitespace <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<rect x="308" y="398" width="30" height="13" rx="6.5" fill="#4C8BF5"/>
<circle cx="331" cy="404.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="421" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="437" font-size="11.5" fill="#9AA0A8">wrap_index <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<rect x="308" y="427" width="30" height="13" rx="6.5" fill="#4C8BF5"/>
<circle cx="331" cy="433.5" r="5" fill="#F5F6F7"/>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

@@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="119" viewBox="0 0 360 119" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Universal Slider Builder (NH) node">
<rect x="0.5" y="0.5" width="359" height="118" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#F6AD55"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Universal Slider Builder (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_UniversalSliderBuilder · Utils</text>
<rect x="12" y="56" width="336" height="46" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="72" font-size="11.5" fill="#9AA0A8">config</text>
<text x="22" y="92" font-size="11.5" fill="#E6E8EB" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace"># Syntax: name, type, default…</text>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

+18
View File
@@ -0,0 +1,18 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="149" viewBox="0 0 360 149" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Unpack Universal (NH) node">
<rect x="0.5" y="0.5" width="359" height="148" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Unpack Universal (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_UnpackUniversal · Utils/Pipe</text>
<circle cx="12" cy="67.0" r="5" fill="#E0B272" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">package <tspan fill="#7d838c" font-style="italic">(optional)</tspan></text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">item</text>
<circle cx="348" cy="67.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<circle cx="12" cy="89.0" r="5" fill="#E0B272" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="93.0" font-size="12" fill="#D7DADF">pipe <tspan fill="#7d838c" font-style="italic">(optional)</tspan></text>
<rect x="12" y="108" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="124" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="124" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="124" font-size="11.5" fill="#9AA0A8">index</text>
<text x="326" y="124" font-size="11.5" fill="#E6E8EB" text-anchor="end">0</text>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

+50
View File
@@ -0,0 +1,50 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="403" viewBox="0 0 360 403" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Load VAE Index (NH) node">
<rect x="0.5" y="0.5" width="359" height="402" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Load VAE Index (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_VAEIndex · Loaders</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">vae</text>
<circle cx="348" cy="67.0" r="5" fill="#FF6E6E" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">filename</text>
<circle cx="348" cy="89.0" r="5" fill="#C5E1A5" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">index</text>
<circle cx="348" cy="111.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="130" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="146" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="146" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="146" font-size="11.5" fill="#9AA0A8">select_mode</text>
<text x="326" y="146" font-size="11.5" fill="#E6E8EB" text-anchor="end">index</text>
<rect x="12" y="159" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="175" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="175" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="175" font-size="11.5" fill="#9AA0A8">index</text>
<text x="326" y="175" font-size="11.5" fill="#E6E8EB" text-anchor="end">1</text>
<rect x="12" y="188" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="204" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="204" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="204" font-size="11.5" fill="#9AA0A8">vae_count</text>
<text x="326" y="204" font-size="11.5" fill="#E6E8EB" text-anchor="end">5</text>
<rect x="12" y="217" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="233" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="233" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="233" font-size="11.5" fill="#9AA0A8">vae_1 <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="326" y="233" font-size="11.5" fill="#E6E8EB" text-anchor="end">(choose a file)</text>
<rect x="12" y="246" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="262" font-size="11.5" fill="#9AA0A8">enable_1 <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<rect x="308" y="252" width="30" height="13" rx="6.5" fill="#4a4d53"/>
<circle cx="315" cy="258.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="275" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="291" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="291" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="291" font-size="11.5" fill="#9AA0A8">vae_2 <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="326" y="291" font-size="11.5" fill="#E6E8EB" text-anchor="end">(choose a file)</text>
<rect x="12" y="304" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="320" font-size="11.5" fill="#9AA0A8">enable_2 <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<rect x="308" y="310" width="30" height="13" rx="6.5" fill="#4a4d53"/>
<circle cx="315" cy="316.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="333" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="180.0" y="349" font-size="11" fill="#9AA0A8" text-anchor="middle" font-style="italic">vae_3 … vae_64</text>
<rect x="12" y="362" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="180.0" y="378" font-size="11" fill="#9AA0A8" text-anchor="middle" font-style="italic">enable_3 … enable_64</text>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

@@ -0,0 +1,76 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="571" viewBox="0 0 360 571" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="VTON Ultimate Processor (NH) node">
<rect x="0.5" y="0.5" width="359" height="570" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">VTON Ultimate Processor (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_VTonUltimateProcessor · VTON</text>
<circle cx="12" cy="67.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">human_image</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">final_mask</text>
<circle cx="348" cy="67.0" r="5" fill="#81C784" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="93.0" font-size="12" fill="#D7DADF" text-anchor="end">agnostic_mask</text>
<circle cx="348" cy="89.0" r="5" fill="#81C784" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="115.0" font-size="12" fill="#D7DADF" text-anchor="end">densepose_image</text>
<circle cx="348" cy="111.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="137.0" font-size="12" fill="#D7DADF" text-anchor="end">parsing_image</text>
<circle cx="348" cy="133.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="159.0" font-size="12" fill="#D7DADF" text-anchor="end">masked_image</text>
<circle cx="348" cy="155.0" r="5" fill="#64B5F6" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="181.0" font-size="12" fill="#D7DADF" text-anchor="end">parsing_map_raw</text>
<circle cx="348" cy="177.0" r="5" fill="#81C784" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="203.0" font-size="12" fill="#D7DADF" text-anchor="end">hair_mask</text>
<circle cx="348" cy="199.0" r="5" fill="#81C784" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="336" y="225.0" font-size="12" fill="#D7DADF" text-anchor="end">hands_mask</text>
<circle cx="348" cy="221.0" r="5" fill="#81C784" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="240" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="256" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="256" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="256" font-size="11.5" fill="#9AA0A8">category</text>
<text x="326" y="256" font-size="11.5" fill="#E6E8EB" text-anchor="end">Upper-body</text>
<rect x="12" y="269" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="285" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="285" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="285" font-size="11.5" fill="#9AA0A8">mask_feathering</text>
<text x="326" y="285" font-size="11.5" fill="#E6E8EB" text-anchor="end">4</text>
<rect x="12" y="298" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="314" font-size="11.5" fill="#9AA0A8">cover_shoes</text>
<rect x="308" y="304" width="30" height="13" rx="6.5" fill="#4a4d53"/>
<circle cx="315" cy="310.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="327" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="343" font-size="11.5" fill="#9AA0A8">refine_hands</text>
<rect x="308" y="333" width="30" height="13" rx="6.5" fill="#4C8BF5"/>
<circle cx="331" cy="339.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="356" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="372" font-size="11.5" fill="#9AA0A8">refine_hair</text>
<rect x="308" y="362" width="30" height="13" rx="6.5" fill="#4C8BF5"/>
<circle cx="331" cy="368.5" r="5" fill="#F5F6F7"/>
<rect x="12" y="385" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="401" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="401" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="401" font-size="11.5" fill="#9AA0A8">device</text>
<text x="326" y="401" font-size="11.5" fill="#E6E8EB" text-anchor="end">cpu</text>
<rect x="12" y="414" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="430" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="430" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="430" font-size="11.5" fill="#9AA0A8">offset_top <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="326" y="430" font-size="11.5" fill="#E6E8EB" text-anchor="end">0</text>
<rect x="12" y="443" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="459" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="459" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="459" font-size="11.5" fill="#9AA0A8">offset_bottom <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="326" y="459" font-size="11.5" fill="#E6E8EB" text-anchor="end">0</text>
<rect x="12" y="472" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="488" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="488" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="488" font-size="11.5" fill="#9AA0A8">offset_left <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="326" y="488" font-size="11.5" fill="#E6E8EB" text-anchor="end">0</text>
<rect x="12" y="501" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="517" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="517" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="517" font-size="11.5" fill="#9AA0A8">offset_right <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="326" y="517" font-size="11.5" fill="#E6E8EB" text-anchor="end">0</text>
<rect x="12" y="530" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="546" font-size="11.5" fill="#9AA0A8">keep_model_loaded <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<rect x="308" y="536" width="30" height="13" rx="6.5" fill="#4a4d53"/>
<circle cx="315" cy="542.5" r="5" fill="#F5F6F7"/>
</svg>

After

Width:  |  Height:  |  Size: 6.1 KiB

+39
View File
@@ -0,0 +1,39 @@
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="301" viewBox="0 0 360 301" font-family="Inter, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" role="img" aria-label="Value Match Index (NH) node">
<rect x="0.5" y="0.5" width="359" height="300" rx="10" fill="#26282c" stroke="#45484f"/>
<path d="M0.5 10.5 a10 10 0 0 1 10 -10 h339 a10 10 0 0 1 10 10 v36 h-359 z" fill="#34373d"/>
<circle cx="17" cy="17" r="4.5" fill="#8FD18F"/>
<text x="28" y="22" font-size="14" font-weight="600" fill="#EEF0F3">Value Match Index (NH)</text>
<text x="28" y="38" font-size="10.5" fill="#9AA0A8" font-family="'JetBrains Mono', Consolas, 'Courier New', monospace">NH_ValueMatchIndex · Logic</text>
<circle cx="12" cy="67.0" r="5" fill="#CFD8DC" stroke="#1b1c1f" stroke-width="1.5"/>
<text x="24" y="71.0" font-size="12" fill="#D7DADF">input</text>
<text x="336" y="71.0" font-size="12" fill="#D7DADF" text-anchor="end">index</text>
<circle cx="348" cy="67.0" r="5" fill="#9CC2E5" stroke="#1b1c1f" stroke-width="1.5"/>
<rect x="12" y="86" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="102" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="102" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="102" font-size="11.5" fill="#9AA0A8">compare_mode</text>
<text x="326" y="102" font-size="11.5" fill="#E6E8EB" text-anchor="end">in</text>
<rect x="12" y="115" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="131" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="131" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="131" font-size="11.5" fill="#9AA0A8">value_count</text>
<text x="326" y="131" font-size="11.5" fill="#E6E8EB" text-anchor="end">5</text>
<rect x="12" y="144" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="160" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="160" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="160" font-size="11.5" fill="#9AA0A8">no_match_index</text>
<text x="326" y="160" font-size="11.5" fill="#E6E8EB" text-anchor="end">-1</text>
<rect x="12" y="173" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="20" y="189" font-size="10" fill="#6f747c">◀</text>
<text x="332" y="189" font-size="10" fill="#6f747c">▶</text>
<text x="34" y="189" font-size="11.5" fill="#9AA0A8">multi_match_index</text>
<text x="326" y="189" font-size="11.5" fill="#E6E8EB" text-anchor="end">-1</text>
<rect x="12" y="202" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="218" font-size="11.5" fill="#9AA0A8">value_1 <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="338" y="218" font-size="11.5" fill="#E6E8EB" text-anchor="end"></text>
<rect x="12" y="231" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="22" y="247" font-size="11.5" fill="#9AA0A8">value_2 <tspan fill="#6f747c" font-style="italic">opt</tspan></text>
<text x="338" y="247" font-size="11.5" fill="#E6E8EB" text-anchor="end"></text>
<rect x="12" y="260" width="336" height="24" rx="7" fill="#1d1f22" stroke="#3a3d43"/>
<text x="180.0" y="276" font-size="11" fill="#9AA0A8" text-anchor="middle" font-style="italic">value_3 … value_64</text>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

Some files were not shown because too many files have changed in this diff Show More