feat: i18n locales (en) + per-node help pages (#2)

Wires two ComfyUI extension points so the nodes are discoverable to
casual users without reading source.

* i18n — `locales/en/` follows ComfyUI's auto-scan layout:
  - `nodeDefs.json` — display names, descriptions, input/output names,
    tooltips for WP_Context, WP_PromptAssembler, WP_Debug.
  - `settings.json` — Reduce motion + Contrast accessibility combos
    keyed as `wildcardPipeline_a11y_*`.
  - `main.json` — settings category labels (Wildcard Pipeline,
    Accessibility).
  English baseline only; structure ready for community PRs to add
  zh/ja/fr/etc. without code changes.
  Docs: https://docs.comfy.org/custom-nodes/i18n

* Help pages — `docs/help/<NodeId>.md` (markdown) gets copied to
  `js/docs/` (the WEB_DIRECTORY) at build time via
  `scripts/copy-help-docs.mjs`. ComfyUI auto-scans
  `${WEB_DIRECTORY}/docs/<NodeId>.md` and surfaces the rendered
  markdown in the node-help panel. Three pages written in casual-user
  voice: what the node does, how to use it, what icons mean — no dev
  jargon (no "last-write-wins", "idempotent", regex, etc.).
  Docs: https://docs.comfy.org/custom-nodes/help_page

* Build pipeline: `pnpm build:extension` now chains
  `vite build && pnpm docs:copy` so a fresh `js/` always carries the
  latest help pages alongside the bundled JS chunks. CI picks this up
  automatically (the same script ran by .github/workflows/ci.yml).
This commit is contained in:
DumiFlex
2026-04-26 05:51:57 +03:00
committed by GitHub
parent 03f61a2b5e
commit 52165471c0
8 changed files with 241 additions and 1 deletions
+41
View File
@@ -0,0 +1,41 @@
# WP Context
Hold a list of named values you can use later in your prompt. Each value gets a name (like `style` or `subject`) and the value you want it to be (like `watercolor` or `a cat`). Then a **WP Prompt Assembler** can plug those values into your prompt wherever you write `$style` or `$subject`.
## Adding values
A **module** is one small group of values. Click the big purple **Add your first module** button, or **+ add module** if you already have some.
Each module appears as a small card. Right-click the card and pick **Edit** to:
- Give it a name (just a label so you remember what's inside, like "Character details")
- Add as many `name = value` lines as you need
- Paste a bunch of lines at once if you have them ready
Press **Enter** on the value field to add another row quickly. **Ctrl+Enter** saves and closes.
## What the icons on a card mean
- **☰** Drag handle — drag a card to reorder, or drag onto another Context node to move it there
- **▾ / ▸** Show or hide the values preview
- **Toggle (checkbox)** — turn a module off without deleting it. Useful for trying variations.
- **✕** Remove the module. Don't worry about misclicks — an **Undo** button appears for a few seconds.
## Stacking multiple Contexts
Connect another **WP Context** node into the **upstream** input. The upstream Context's values flow through. If both define the same name, this Context wins (it's the closer one to your prompt).
This lets you build a library: "character" Context → "scene" Context → "style" Context → Prompt Assembler.
## Conflict dots
A card may show a small colored dot:
- 🔵 **Blue (info)** — You're overriding something an upstream Context already provides. Usually that's exactly what you want.
- 🟡 **Amber (warning)** — Two of your modules in this Context use the same name. One quietly overwrites the other; check that's intentional.
Hover the dot to see exactly which variable.
## Output
Wire **context** out into a **WP Prompt Assembler** (or another **WP Context** to chain).
+22
View File
@@ -0,0 +1,22 @@
# WP Debug
Drop this anywhere in your pipeline to see exactly what variables are flowing through that point. Like a window into the chain.
## How to use
1. Connect the **context** input to any WP node (Context or Prompt Assembler).
2. Run the workflow once.
3. The viewer below shows every variable name and its value at that point in the chain.
That's it. There's nothing to wire on the output side — Debug is read-only.
## When it's useful
- **"Why is `$style` showing as empty?"** Drop a Debug after each Context to see where the value gets lost or overridden.
- **"Did my new module override the old one correctly?"** The trace shows which module set each variable.
- **"Are upstream variables coming through the chain?"** Drop a Debug right before your Prompt Assembler to confirm.
## Tips
- The viewer fills the node body. Drag the bottom-right corner of the node to make it bigger if you have lots of variables.
- Debug nodes always re-run, even when nothing visually changed. Helpful when you're cycling seeds and want to see the values shift.
+47
View File
@@ -0,0 +1,47 @@
# WP Prompt Assembler
Write your prompt as a fill-in-the-blank template. Anywhere you put `$something`, the Assembler swaps in the matching value from the upstream Context.
## Writing a template
Type your prompt in the **template** box. Use `$name` to reference any variable from the connected Context.
**Example template:**
```
a portrait of $subject, $style, $mood
```
If the upstream Context provides:
- `subject = a cat`
- `style = watercolor`
- `mood = sleepy`
…the final prompt becomes:
```
a portrait of a cat, watercolor, sleepy
```
## The helper below the template
**Variables** lists every value available from the upstream Context. Click any chip to insert `$name` into your template at the cursor — saves typing and avoids typos.
**Preview** shows what the final prompt will look like, color-coded:
- **Filled in (purple highlight)** — the variable is resolved, you see its actual value.
- **Outlined chip** — the name will resolve, just briefly during loading.
- **Amber wavy underline** — missing! Nothing upstream provides this name, so it'll render empty.
If your template is empty, the preview shows a placeholder instead.
## Special characters
- Use `$$` if you want a literal `$` in your prompt (e.g. `$$10` to mean "ten dollars" rather than a variable named `10`).
- Variable names can contain letters, numbers, and underscores only. `$my_var` works; `$my-var` doesn't.
## Output
**prompt** is the final assembled string. Connect it to a **CLIP Text Encode** node (or any text input).
## Pre-flight warnings
When you press the **Run** button, the extension scans your template for `$variables` that aren't provided anywhere upstream. If it finds any, you'll see one toast at the top of the screen listing them. The run still happens — missing variables just become empty strings — but it's a heads-up in case you have a typo.
+6
View File
@@ -0,0 +1,6 @@
{
"settingsCategories": {
"Wildcard Pipeline": "Wildcard Pipeline",
"Accessibility": "Accessibility"
}
}
+60
View File
@@ -0,0 +1,60 @@
{
"WP_Context": {
"display_name": "WP Context",
"description": "Holds named values you can plug into a prompt template. Wire into a WP Prompt Assembler to use them. Stack multiple Contexts to layer values from different sources.",
"inputs": {
"upstream": {
"name": "upstream",
"tooltip": "Optional: another WP Context to inherit values from. Values you set here override matching ones from upstream."
},
"seed": {
"name": "seed",
"tooltip": "Reserved for upcoming randomized module types. Has no effect on the current fixed-values modules."
},
"modules": {
"name": "modules",
"tooltip": "Your stack of value modules. Right-click a card → Edit to add or change values."
}
},
"outputs": {
"0": {
"name": "context",
"tooltip": "Wire into a WP Prompt Assembler, or chain into another WP Context."
}
}
},
"WP_PromptAssembler": {
"display_name": "WP Prompt Assembler",
"description": "Write your prompt as a template with $variable tokens. The Assembler fills them in from the connected Context, then outputs the final prompt string.",
"inputs": {
"context": {
"name": "context",
"tooltip": "The WP Context that provides values for your $variables."
},
"template": {
"name": "template",
"tooltip": "Your prompt template. Use $name to reference variables. Click chips below to insert at the cursor."
}
},
"outputs": {
"0": {
"name": "prompt",
"tooltip": "Final prompt string with all $variables filled in. Connect to a CLIP Text Encode (or any text input)."
}
}
},
"WP_Debug": {
"display_name": "WP Debug",
"description": "Read-only window into a WP Context. Shows every variable + value flowing through this point of the chain. Useful for confirming what your Prompt Assembler will see.",
"inputs": {
"context": {
"name": "context",
"tooltip": "The WP Context (or Assembler input) to inspect."
},
"viewer": {
"name": "viewer",
"tooltip": "Read-only display. Nothing to edit here."
}
}
}
}
+20
View File
@@ -0,0 +1,20 @@
{
"wildcardPipeline_a11y_reduceMotion": {
"name": "Reduce motion",
"tooltip": "Disables Wildcard Pipeline animations and transitions. Match system honors the OS prefers-reduced-motion setting.",
"options": {
"auto": "Match system (prefers-reduced-motion)",
"on": "Always reduce",
"off": "Always allow"
}
},
"wildcardPipeline_a11y_contrast": {
"name": "Contrast",
"tooltip": "Bumps border + text contrast on Wildcard Pipeline widgets. Match system honors the OS prefers-contrast setting.",
"options": {
"auto": "Match system (prefers-contrast)",
"on": "High contrast",
"off": "Standard"
}
}
}
+2 -1
View File
@@ -9,9 +9,10 @@
"packageManager": "pnpm@9.12.0",
"scripts": {
"dev": "vite build --watch --mode extension",
"build:extension": "vite build --mode extension",
"build:extension": "vite build --mode extension && pnpm docs:copy",
"build:manager": "vite build --mode manager",
"build": "pnpm build:extension && pnpm build:manager",
"docs:copy": "node scripts/copy-help-docs.mjs",
"typecheck": "vue-tsc --noEmit",
"lint": "eslint \"src/**/*.{ts,vue}\"",
"size": "node scripts/check-bundle-size.mjs",
+43
View File
@@ -0,0 +1,43 @@
#!/usr/bin/env node
// Copy markdown help pages from docs/help/ to js/docs/ so ComfyUI's
// node-help-page system can serve them under /extensions/<package>/docs/.
// ComfyUI auto-scans `${WEB_DIRECTORY}/docs/<NodeId>.md` (plus optional
// `<NodeId>/<locale>.md` overrides) — see
// https://docs.comfy.org/custom-nodes/help_page
//
// Run as part of `pnpm build:extension` so a fresh `js/` always has the
// latest docs alongside the bundled JS chunks.
import { readdirSync, statSync, mkdirSync, copyFileSync, rmSync } from "node:fs";
import { join, relative } from "node:path";
import { fileURLToPath } from "node:url";
const ROOT = fileURLToPath(new URL("..", import.meta.url));
const SRC = join(ROOT, "docs", "help");
const DST = join(ROOT, "js", "docs");
function walk(dir) {
const out = [];
for (const name of readdirSync(dir)) {
const full = join(dir, name);
if (statSync(full).isDirectory()) out.push(...walk(full));
else out.push(full);
}
return out;
}
try {
rmSync(DST, { recursive: true, force: true });
const files = walk(SRC);
for (const file of files) {
const rel = relative(SRC, file);
const target = join(DST, rel);
mkdirSync(join(target, ".."), { recursive: true });
copyFileSync(file, target);
console.log(` docs/help/${rel} → js/docs/${rel}`);
}
console.log(`Copied ${files.length} help file(s).`);
} catch (err) {
console.error("[copy-help-docs] failed:", err);
process.exit(1);
}