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:
@@ -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).
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"settingsCategories": {
|
||||
"Wildcard Pipeline": "Wildcard Pipeline",
|
||||
"Accessibility": "Accessibility"
|
||||
}
|
||||
}
|
||||
@@ -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."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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",
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user