Files
DumiFlex 18e9243542 feat(tutorials): review round 3 (callout hold rule, A17 rewrite, AI3 image ending, starter library) (#357)
Callout hold rule, dead-pause fix after runs, A17 rewrite (toggle variables first), AI3 ends by rendering with the built pack's top bundle, AI1 line, box lines in A16/T10/T12, N13 at 20x, and three starter-library items (Daydream, Lights on or off, Shot per frame).
2026-11-11 22:58:26 +01:00

132 lines
6.1 KiB
TypeScript

// Tutorial 12: where to find help. The ? over a selected node, the example workflows in ComfyUI's
// Templates, the button that opens the manager, the built-in docs, the shortcut sheet and command
// palette, and What's new after an update.
import { tutorial, hideOtherPacks } from "../tutorial.js";
tutorial(
"T12",
{
title: "Where to find help",
description: "Node help on the canvas, the example workflows, the manager's documentation, keyboard shortcuts, the command palette and What's new.",
learn: "Every place the extension explains itself, and the ready-made example workflows.",
},
async (t) => {
const page = t.page;
await t.h.step("Help on a node", async () => {
await t.go("/");
await t.say("Last in the basics: where to look when you're stuck. Every Wildcard Pipeline node has its own help.");
const ctx = await t.addNode("WP Context", { x: 700, y: 300 });
await t.beat(500);
const box = await t.node(ctx).boundingBox();
if (box) await t.click({ x: box.x + box.width / 2, y: box.y + 14 });
await t.beat(500);
const help = page.locator(".wp-tb-icon--help").first().locator("xpath=ancestor::button[1]");
await t.callout("help", help, "This node's help", 6);
// The box stays up for the whole line and the click comes after it, so the two line up.
await t.say("Select it and press the question mark above it.");
await t.waitVoice();
await t.beat(300);
await t.click(help);
await t.uncallout("help");
await t.say("Its page opens over the canvas: what each part does, with a picture.");
await t.beat(1500);
await t.waitVoice();
await t.press("Escape");
await t.beat(600);
});
await t.h.step("Example workflows", async () => {
await t.say("Want to see it all working together? There are four example workflows, in ComfyUI's Templates.");
await t.click(page.getByRole("button", { name: /^templates/i }).first());
await t.beat(1200);
const dialog = page.getByRole("dialog").last();
const ours = dialog.getByText("ComfyUI-Wildcard-Pipeline", { exact: true });
await ours.waitFor({ state: "attached", timeout: 15_000 });
await hideOtherPacks(ours);
// It sits at the bottom of the list, below the window's edge on a 1080p screen.
await t.moveTo({ x: 370, y: 700 });
await ours.scrollIntoViewIfNeeded();
await t.beat(400);
await t.click(ours);
await t.beat(1000);
await t.waitVoice();
await t.callout("examples", dialog.getByText("1 - One image", { exact: true }).locator("xpath=ancestor::*[contains(@class,'grid')][1]"), "Four ready-made workflows", 6);
await t.say("Under Extensions, Wildcard Pipeline. Each one runs on an empty library, with notes that walk you through it. Open one, pick your checkpoint and press Run.");
await t.waitVoice();
await t.uncallout("examples");
await t.say("The example workflow videos go through each of them.");
await t.press("Escape");
// The closing dialog still covers the toolbar for a moment, and a box drawn over it is taken
// for hidden and dropped at once (the "Opens the manager" box flashed for half a second).
await dialog.waitFor({ state: "hidden", timeout: 5_000 }).catch(() => undefined);
await t.waitVoice();
await t.beat(400);
});
await t.h.step("The manager's docs", async () => {
const open = page.getByRole("button", { name: /Wildcard Pipeline Workspace/ }).first();
if ((await open.count()) > 0) {
// The box comes up with its line and stays for all of it.
await t.say("This button opens the manager. Its Documentation covers every node and every module, with examples.");
await t.callout("open", open, "Opens the manager", 6);
await t.waitVoice();
await t.uncallout("open");
}
await t.go("/wp/docs");
await t.say("Here it is. The list on the left has how-tos, guides, each node and each module.");
await t.waitVoice();
await t.click(page.getByRole("link", { name: "Start from an example" }).first());
await t.beat(1200);
await t.say("Search finds any page; the AI assistant reads these pages too, if you set one up.");
await t.waitVoice();
});
await t.h.step("Shortcuts", async () => {
await t.say("Control and slash shows every keyboard shortcut.");
await t.press("Control+/");
await t.beat(1200);
// The dialog role sits on a full-width wrapper; box the panel inside it.
await t.waitVoice();
// The box gets its own line (it came at the end of the last one and flashed past).
await t.callout("keys", page.getByRole("dialog").last().locator(".wp-modal").first(), "Keyboard shortcuts", 4);
await t.say("Here they all are, in one list.");
await t.waitVoice();
await t.beat(500);
await t.uncallout("keys");
await t.press("Escape");
await t.beat(500);
await t.say("And Control K opens the command palette: type what you want, and go there.");
await t.press("Control+k");
await t.beat(700);
await t.type("bundles", { cps: 12 });
await t.beat(1200);
await t.waitVoice();
await t.press("Escape");
await t.beat(500);
});
await t.h.step("What's new", async () => {
const news = page.locator('[data-test="topbar-whats-new"]');
await t.callout("news", news, "What's new", 6);
await t.say("After an update, the megaphone gets a dot. It shows what changed in each version.");
await t.waitVoice();
await t.beat(300);
await t.click(news);
await t.uncallout("news");
await t.beat(1500);
const discord = page.locator('[data-test="topbar-discord"]');
if ((await discord.count()) > 0) {
await t.callout("discord", discord, "Ask on Discord", 4);
await t.say("And for anything else, the Discord button is right next to it.");
await t.waitVoice();
await t.uncallout("discord");
}
});
await t.h.step("Outro", async () => {
await t.say("That's the basics. The advanced videos take each part further, one at a time.");
});
},
);