fix: hints and labels now say what the control does (menus, inspector, timeline, takes, ideas, enhancer, context windows, montage, modes); H3 project negative prompt reaches the negative slot
This commit is contained in:
@@ -24,7 +24,7 @@ export default {
|
||||
let volume = t.volume != null ? t.volume : 1, start = t.start_sec || 0;
|
||||
const body = c.region.stack({ gap: "sm", children: [
|
||||
c.hint.default({ text: separated ? "This clip's own sound. It follows its clip; slide or trim it on the timeline." : "A sound over the cut. Slide and trim it on the timeline." }),
|
||||
c.field.default({ label: "Volume", control: c.slider.readout({ label: "Volume", value: volume, min: 0, max: 2, step: 0.05, precision: 2, onCommit: (v) => { volume = v; } }) }),
|
||||
c.field.default({ label: "Volume", hint: "1 = as recorded. Above 1 makes the rendered sound louder; the monitor plays at most 1.", control: c.slider.readout({ label: "Volume", value: volume, min: 0, max: 2, step: 0.05, precision: 2, onCommit: (v) => { volume = v; } }) }),
|
||||
separated ? null : c.field.default({ label: "Starts at (s)", control: c.number.md({ label: "Starts at", value: start, min: 0, step: 0.1, onChange: (v) => { start = v; } }) }),
|
||||
].filter(Boolean) });
|
||||
const win = c.modal.generic({ title: t.label || "Audio track", size: "sm", body });
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
import { composer as c } from "../../composer/composer.js";
|
||||
|
||||
const NODE = "FunPackEnhancePrompt";
|
||||
const DIALS = [["temperature", 0.7, 0, 2, 0.05], ["top_p", 0.92, 0, 1, 0.01], ["top_k", 50, 1, 1000, 1], ["min_p", 0.05, 0, 1, 0.01], ["repetition_penalty", 1.3, 1, 2, 0.05], ["presence_penalty", 0, 0, 2, 0.05]];
|
||||
const DIALS = [["temperature", 0.7, 0, 2, 0.05, "Higher gives more varied wording, lower more predictable."], ["top_p", 0.92, 0, 1, 0.01, "Picks only among the likeliest words whose chances add up to this share."],
|
||||
["top_k", 50, 1, 1000, 1, "Picks only among this many likeliest words."], ["min_p", 0.05, 0, 1, 0.01, "Drops words much less likely than the likeliest one; higher drops more."],
|
||||
["repetition_penalty", 1.3, 1, 2, 0.05, "Above 1 discourages repeating words."], ["presence_penalty", 0, 0, 2, 0.05, "Above 0 discourages words already used."]];
|
||||
|
||||
export function enhance(app, own) {
|
||||
const ps = app.pipeline, page = c.region.stack({ gap: "sm" });
|
||||
@@ -12,7 +14,7 @@ export function enhance(app, own) {
|
||||
const slot = () => (ps.slots() || []).find((s) => s.node === NODE) || null;
|
||||
const set = async (name, value) => { const sl = slot(); if (sl) { await ps.save({ inputs: { [sl.id]: { [name]: value } } }); draw(); } };
|
||||
const field = (label, control, hint) => c.field.default({ label, control, hint });
|
||||
const num = (label, v, lo, hi, step, name) => field(label, c.number.md({ label, value: v, min: lo, max: hi, step, onChange: (x) => set(name, x) }));
|
||||
const num = (label, v, lo, hi, step, name, hint) => field(label, c.number.md({ label, value: v, min: lo, max: hi, step, onChange: (x) => set(name, x) }), hint);
|
||||
|
||||
function readout() {
|
||||
if (!last) return [c.label.section({ text: "Last run" }), c.hint.default({ text: "Nothing yet. The rewrite of a run shows here." })];
|
||||
@@ -32,10 +34,10 @@ export function enhance(app, own) {
|
||||
c.toggle.default({ label: "Enhance prompt", checked: on, onChange: (x) => set("enabled", x) }),
|
||||
...(!on ? readout() : [
|
||||
field("Instructions", c.textarea.md({ label: "Instructions", rows: 6, value: val("instructions", ""), placeholder: defaults ? defaults.instructions : "(built-in instructions)", onCommit: (x) => set("instructions", x) }), "Empty uses the built-in instructions."),
|
||||
num("Max tokens", val("max_length", 400), 32, 4096, 8, "max_length"),
|
||||
num("Max tokens", val("max_length", 400), 32, 4096, 8, "max_length", "The longest the rewrite may be."),
|
||||
c.toggle.default({ label: "Use the picture", hint: "Sends the scene's picture to the model along with the prompt.", checked: val("use_image", true), onChange: (x) => set("use_image", x) }),
|
||||
c.toggle.default({ label: "Thinking", hint: "Let a thinking model reason first. Its reasoning eats the token budget.", checked: val("thinking", false), onChange: (x) => set("thinking", x) }),
|
||||
c.toggle.default({ label: "Greedy (always the likeliest word)", checked: greedy, onChange: (x) => set("greedy", x) }),
|
||||
c.toggle.default({ label: "Greedy (always the likeliest word)", hint: "The same rewrite every time; the dials below are not used.", checked: greedy, onChange: (x) => set("greedy", x) }),
|
||||
...(greedy ? [] : DIALS.map(([n, d, lo, hi, st]) => num(n, val(n, d), lo, hi, st, n))),
|
||||
num("Seed", val("seed", 0), 0, 2 ** 53, 1, "seed"),
|
||||
c.hint.default({ text: "Seed 0 = a fresh rewrite every Generate; any other number repeats the same rewrite." }),
|
||||
|
||||
@@ -150,10 +150,10 @@ export default {
|
||||
if (key === drawn) return; // progress ticks must not rebuild the buttons
|
||||
drawn = key;
|
||||
const working = busy || held || g.state().phase === QUEUED || g.state().phase === RUNNING; // also a run this page did not start
|
||||
const go = c.button.sm({ label: working ? "Generating…" : "▶ Generate", tone: "primary", disabled: working || !open,
|
||||
const go = c.button.sm({ label: working ? "Generating…" : "▶ Generate", tone: "primary", disabled: working || !open, title: "Generate every scene on the timeline, except left-out scenes and video clips. An earlier render of a scene stays as a take.",
|
||||
onClick: () => runUnits(unitsOf(p.scenes)) });
|
||||
const one = c.button.sm({ label: picked > 1 ? `Selected (${picked})` : "Selected", disabled: working || !picked, onClick: () => runUnits(unitsOf(pickedScenes())) });
|
||||
const stop = c.button.sm({ label: "■ Stop", tone: "danger", onClick: () => { stopped = true; g.cancel(); } });
|
||||
const one = c.button.sm({ label: picked > 1 ? `Selected (${picked})` : "Selected", title: "Generate only the picked scenes", disabled: working || !picked, onClick: () => runUnits(unitsOf(pickedScenes())) });
|
||||
const stop = c.button.sm({ label: "■ Stop", tone: "danger", title: "Stop the run that is going", onClick: () => { stopped = true; g.cancel(); } });
|
||||
stop.node.hidden = !working;
|
||||
put(go.node, one.node, stop.node);
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ export default {
|
||||
window.addEventListener("scroll", onMove, true); window.addEventListener("resize", onMove);
|
||||
|
||||
app.editorSettings.push(() => c.region.stack({ gap: "sm", children: [
|
||||
c.toggle.default({ label: "Shortcut ideas", hint: "A 💡 beside prompt boxes lights up when shortcut categories in your library aren't used in the prompt. Click it for insertable ideas; it never pops up on its own.",
|
||||
c.toggle.default({ label: "Shortcut ideas", hint: "Shows a 💡 beside the Story and Prompt boxes while you are in them. Click it for shortcuts to insert: pairings you use, what usually follows the previous shot, and categories this prompt does not use yet.",
|
||||
checked: on(), onChange: (v) => { p.setPref("suggestions", v); if (!v) closeBulb(); } })] }));
|
||||
return () => { closeBulb(); clearTimeout(hold); document.removeEventListener("focusin", onFocus); document.removeEventListener("focusout", onBlur); window.removeEventListener("scroll", onMove, true); window.removeEventListener("resize", onMove); };
|
||||
},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// "+ Import" on the timeline toolbar: a video from the media bin as a clip of its own, or a sound as an audio track at the playhead.
|
||||
// "+ From bin" on the timeline toolbar: a video from the media bin as a clip of its own, or a sound as an audio track at the playhead.
|
||||
import { composer as c } from "../../composer/composer.js";
|
||||
import { addVideoClip, addAudioTrack } from "../../shell/imports.js";
|
||||
import { learn } from "../../shell/bin.js";
|
||||
@@ -42,7 +42,7 @@ export default {
|
||||
if (seconds == null) c.toast.warn({ text: asset.kind === "video" ? "The file's length could not be read; the clip uses the project's length." : "The file's length could not be read: the lane cannot be trimmed, and plays to the end of the file." });
|
||||
}
|
||||
|
||||
const menu = c.button.menu({ label: "+ Import", tone: "ghost", onClick: () => c.menu.dropdown({ anchor: menu, items: [{ id: "video", label: "Video clip…", disabled: !p.project }, { id: "audio", label: "Audio track…", disabled: !p.project }],
|
||||
const menu = c.button.menu({ label: "+ From bin", tone: "ghost", onClick: () => c.menu.dropdown({ anchor: menu, items: [{ id: "video", label: "Video clip…", disabled: !p.project }, { id: "audio", label: "Audio track…", disabled: !p.project }],
|
||||
onPick: (id) => choose(id) }) });
|
||||
host.append(menu.node);
|
||||
|
||||
|
||||
@@ -70,12 +70,12 @@ export function projectRows(p, slots, api) {
|
||||
c.hint.default({ text: `≈ ${per.toFixed(2)} s per shot` }),
|
||||
size.length ? c.field.row({ fields: size.map((x) => x.field) }) : null,
|
||||
...custom(p, open),
|
||||
c.field.default({ label: "Final render size", control: c.select.md({ label: "Final render size", options: sizes(open), value: open.export_size_from || "",
|
||||
c.field.default({ label: "Final render size", hint: "The size of the exported video: the first clip's, the project's, or any rendered clip's own.", control: c.select.md({ label: "Final render size", options: sizes(open), value: open.export_size_from || "",
|
||||
onChange: (v) => p.setField("export_size_from", v) }) }),
|
||||
c.field.default({ label: "Start shots", control: c.select.md({ label: "Start shots", options: STARTS, value: open.generation_mode === "t2v" ? "t2v" : "i2v", onChange: (v) => p.setField("generation_mode", v) }) })),
|
||||
c.field.default({ label: "Start shots", hint: "From an image: a scene starts from its picture, or the last frame of the clip before it. From a prompt: every scene starts without a picture.", control: c.select.md({ label: "Start shots", options: STARTS, value: open.generation_mode === "t2v" ? "t2v" : "i2v", onChange: (v) => p.setField("generation_mode", v) }) })),
|
||||
c.label.section({ text: "Prompt" }),
|
||||
card(c.field.default({ label: "Anchor", control: c.textarea.md({ label: "Anchor", value: p.anchor, rows: 2, onInput: (v) => p.setAnchor(v) }) }),
|
||||
c.field.default({ label: "Negative prompt", control: c.textarea.md({ label: "Negative prompt", value: p.negative, rows: 2, onInput: (v) => p.setNegative(v) }) }),
|
||||
card(c.field.default({ label: "Anchor", hint: "Put in front of every scene's prompt.", control: c.textarea.md({ label: "Anchor", value: p.anchor, rows: 2, onInput: (v) => p.setAnchor(v) }) }),
|
||||
c.field.default({ label: "Negative prompt", hint: "What to steer away from. A model that ignores negative prompts (MiniMax H3) uses it only with Shadow negative or Negative erase on.", control: c.textarea.md({ label: "Negative prompt", value: p.negative, rows: 2, onInput: (v) => p.setNegative(v) }) }),
|
||||
c.toggle.default({ label: "Postfix", hint: "Appended to every scene (a style or quality tag, say).", checked: open.postfix_enabled !== false, onChange: (v) => p.setField("postfix_enabled", v) }),
|
||||
c.textarea.md({ label: "Postfix", value: p.postfix, rows: 2, disabled: open.postfix_enabled === false, onInput: (v) => p.setPostfix(v) })),
|
||||
c.collapsible.default({ label: "ADVANCED PROJECT SETTINGS", body: c.region.stack({ gap: "sm", children: [
|
||||
|
||||
@@ -52,15 +52,15 @@ export function sceneRows(p, app) {
|
||||
if (!sc) return [c.emptyState.default({ icon: "▭", title: "No scene", hint: "Add one on the timeline." })];
|
||||
const cuts = p.scenes.filter((s) => genUnitId(s) === genUnitId(sc)).length > 1;
|
||||
return [
|
||||
c.banner.info({ text: "Generated by this project's pipeline", action: { label: "Engine settings →", onClick: () => app.openSettings && app.openSettings("engine") } }),
|
||||
c.banner.info({ text: "Made with the project's pipeline and Engine settings", action: { label: "Engine settings →", onClick: () => app.openSettings && app.openSettings("engine") } }),
|
||||
...(sc.removed_from_plan ? [c.banner.info({ text: "Removed from plan — its generated clip stays on the timeline.", action: { label: "Restore to plan", onClick: () => p.edit((pr) => restoreToPlan(pr, sc.id)) } })] : []),
|
||||
...(cuts ? [c.hint.default({ text: "This scene has editorial cuts — Generate regens the whole uncut scene." })] : []),
|
||||
...(isSubclip(sc) ? [c.hint.default({ text: "This is a cut: its prompt and source belong to the first part." })] : [card(
|
||||
c.field.default({ label: "Prompt", control: c.textarea.md({ label: "Prompt", value: sc.text || "", rows: 4, onInput: (v) => p.setText(sc.id, v) }) }),
|
||||
c.field.default({ label: "Source", control: c.select.md({ label: "Source", options: SOURCES, value: (sc.source || {}).type || "carry",
|
||||
onChange: (v) => p.setScene(sc.id, "source", { ...(sc.source || {}), type: v }) }) }))]),
|
||||
c.label.section({ text: "Reference media (v5 pipeline)" }),
|
||||
card(mediaField(app, p, sc, { label: "Aspect ratio source", hint: "Sets this scene's aspect ratio for generation — the project's own Width/Height set the actual resolution; this image's pixels are not used.",
|
||||
c.label.section({ text: "Pictures" }),
|
||||
card(mediaField(app, p, sc, { label: "Aspect ratio source", hint: "The scene's picture. Its shape sets this scene's aspect ratio (the project's Width/Height set the scale). A model that starts from a picture, such as LTX image-to-video, also begins the clip from it; the prompt enhancer can read it.",
|
||||
key: "source_image", multiple: false, kinds: ["image"], empty: "— choose aspect ratio source —" }),
|
||||
mediaField(app, p, sc, { label: "References", key: "references", multiple: true, kinds: ["image", "video", "audio"], empty: "+ Add reference" })),
|
||||
c.label.section({ text: "Length" }),
|
||||
|
||||
@@ -66,14 +66,14 @@ export default {
|
||||
{ id: "delete", label: "Delete Current Project", danger: true, disabled: !p.project }];
|
||||
},
|
||||
Edit: () => [{ id: "undo", label: "Undo", hint: "⌘Z", disabled: !p.canUndo }, { id: "redo", label: "Redo", hint: "⇧⌘Z", disabled: !p.canRedo }, { separator: true },
|
||||
{ id: "add", label: "Add Scene", disabled: !p.project }, { id: "del", label: "Delete Scene", disabled: !sel.ids.length }, { separator: true },
|
||||
{ id: "left", label: "Move Clip Left", hint: "timeline cut", disabled: !sel.focus }, { id: "right", label: "Move Clip Right", hint: "timeline cut", disabled: !sel.focus }, { separator: true },
|
||||
{ id: "add", label: "Add Scene", disabled: !p.project }, { id: "del", label: sel.ids.length > 1 ? `Delete ${sel.ids.length} Scenes` : "Delete Scene", disabled: !sel.ids.length }, { separator: true },
|
||||
{ id: "left", label: "Move Clip Left", hint: "earlier", disabled: !sel.focus }, { id: "right", label: "Move Clip Right", hint: "later", disabled: !sel.focus }, { separator: true },
|
||||
{ id: "exclude", label: "Toggle Exclude", disabled: !sel.focus }],
|
||||
View: () => [{ id: "refresh", label: "Refresh Preview" }, { id: "reset", label: "Reset Layout" }],
|
||||
Help: () => {
|
||||
const running = Boolean(app.tourOn?.());
|
||||
return [...(app.has.has("tour") ? [{ id: "tour", label: running ? "Restart tour" : "Start tour" }, { id: "faq", label: "Skip to FAQ" },
|
||||
...(running ? [{ id: "exit", label: "Exit tour" }] : []), { separator: true }] : []), { id: "welcome", label: "Welcome tour…" }];
|
||||
...(running ? [{ id: "exit", label: "Exit tour" }] : []), { separator: true }] : []), { id: "welcome", label: "Welcome window…" }];
|
||||
},
|
||||
};
|
||||
const off = app.on((what) => { if (what === "project.load") actions.load(); });
|
||||
|
||||
@@ -28,8 +28,8 @@ export default {
|
||||
c.field.default({ label: "Lead clip (plays in order)", control: c.select.md({ label: "Lead clip", value: lead, options: list.map((s) => ({ value: s.id, label: label(s) })), onChange: (v) => { lead = v; pool.delete(v); draw(); } }) }),
|
||||
c.field.default({ label: "Cutaway pool (random draw, in order within each clip)", control: c.checklist.default({ label: "Cutaway pool", values: [...pool],
|
||||
items: list.filter((s) => s.id !== lead).map((s) => ({ value: s.id, label: label(s) })), onChange: (v) => { pool.clear(); v.forEach((x) => pool.add(x)); } }) }),
|
||||
c.field.default({ label: "Segment length (frames)", control: c.number.md({ label: "Segment length", value: seg, min: 9, step: 1, precision: 0, onChange: (v) => { seg = v; } }) }),
|
||||
c.field.default({ label: "Acceleration", hint: "1.0 = constant, lower = faster cuts toward the end", control: c.slider.readout({ label: "Acceleration", value: decay, min: 0.5, max: 1, step: 0.05, onCommit: (v) => { decay = v; } }) }),
|
||||
c.field.default({ label: "Segment length (frames)", hint: "How long the lead clip's first piece and each cutaway piece are.", control: c.number.md({ label: "Segment length", value: seg, min: 9, step: 1, precision: 0, onChange: (v) => { seg = v; } }) }),
|
||||
c.field.default({ label: "Acceleration", hint: "1.0 = equal segments; lower = the lead clip's segments get shorter toward the end", control: c.slider.readout({ label: "Acceleration", value: decay, min: 0.5, max: 1, step: 0.05, onCommit: (v) => { decay = v; } }) }),
|
||||
]);
|
||||
draw();
|
||||
const win = c.modal.generic({ title: "Auto Montage", subtitle: "Cuts the lead clip into shrinking segments and randomly inserts cutaways between them.", size: "md", body });
|
||||
|
||||
@@ -29,7 +29,7 @@ export default {
|
||||
} catch (err) { tell(err.message); } finally { busy = false; draw(); }
|
||||
}
|
||||
function draw() {
|
||||
const next = c.button.sm({ label: busy ? "Rendering…" : "⧉ Render", tone: "render", disabled: busy || !p.project, onClick: render }).node;
|
||||
const next = c.button.sm({ label: busy ? "Rendering…" : "⧉ Render", tone: "render", title: "Join the generated clips into one video and save it in the media bin", disabled: busy || !p.project, onClick: render }).node;
|
||||
if (node) node.replaceWith(next); else host.append(next);
|
||||
node = next;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ export default {
|
||||
};
|
||||
app.pipeline.ensureLoaded().then(offerShot);
|
||||
app.pipeline.subscribe(offerShot);
|
||||
add("system", "Updates & ComfyUI", "Server connection, FunPack code updates, pipeline health.", "⟳", "good",
|
||||
add("system", "Updates & ComfyUI", "Update FunPack, switch its branch, roll back, or restart ComfyUI.", "⟳", "good",
|
||||
system(app), "System", "update git branch restart rollback");
|
||||
add("customnodes", "Custom Nodes", "Install, update and remove ComfyUI node packs.", "⧉", "neutral",
|
||||
packs(app), "System", "custom nodes packs install");
|
||||
|
||||
@@ -38,7 +38,7 @@ export const taste = (app) => function mount() {
|
||||
page.set([
|
||||
error ? c.banner.warn({ text: `Could not read the keys: ${error}` }) : null,
|
||||
keys && !keys.length && !error ? c.emptyState.default({ icon: "✦", title: "No keys yet", hint: "A key appears once you rate a render with learning on." }) : null,
|
||||
...(keys || []).map((name) => c.settingsRow.default({ label: name, hint: "Refinement key", control: c.toolbar.default({ items: [
|
||||
...(keys || []).map((name) => c.settingsRow.default({ label: name, hint: "Taste key: what your ratings taught under this name", control: c.toolbar.default({ items: [
|
||||
c.button.sm({ label: "⤓ Export", tone: "ghost", onClick: () => exportKey(name) }),
|
||||
c.button.sm({ label: "Delete", tone: "danger", onClick: () => remove(name) })] }) })),
|
||||
c.label.section({ text: "Bring a key here" }),
|
||||
|
||||
@@ -31,7 +31,7 @@ export default {
|
||||
const items = list.map((t, i) => ({ id: String(i), label: `Take ${i + 1}`, thumb: still(t), icon: "▶", badge: mark(i === at ? takesOf(pr, sc).head.rating : t.rating) || undefined }));
|
||||
return [c.label.section({ text: "Takes" }),
|
||||
c.gallery.adaptive({ id: "takes", items, selection: [String(at)], cols: 3, empty: "", onActivate: (it) => p.edit((x) => pickTake(x, x.scenes.find((s) => s.id === sc.id), Number(it.id))) }),
|
||||
c.field.default({ label: `Take ${at + 1} of ${list.length}`, hint: "Step through the renders of this scene; the one showing is the one on the clip. Rate each, the best stays.",
|
||||
c.field.default({ label: `Take ${at + 1} of ${list.length}`, hint: "Step through the renders of this scene; the one showing is the one on the clip. Each take keeps its own rating.",
|
||||
control: c.toolbar.default({ items: [c.button.sm({ label: "‹ Earlier", disabled: at === 0, onClick: go(-1) }), c.button.sm({ label: "Later ›", disabled: at === list.length - 1, onClick: go(1) })] }) })];
|
||||
},
|
||||
};
|
||||
|
||||
@@ -80,7 +80,7 @@ const tools = {
|
||||
const open = p.project, ids = open ? sel.ids : [];
|
||||
put(c.hint.default({ text: `${clock(head.at)} / ${clock(open ? totalSeconds(open) : 0)}` }).node,
|
||||
btn("+ Add", () => { const sc = p.edit((pr) => edits.addScene(pr)); if (sc) p.select(sc.id); }, !open),
|
||||
btn("Split", split, !ids.length, "Split the selected clip at the playhead"),
|
||||
btn("Split", split, !ids.length, "Cut the selected clip at the playhead (in the middle when the playhead is elsewhere)"),
|
||||
btn("Remove", () => ids.forEach((id) => p.edit((pr) => edits.removeScene(pr, id))), !ids.length),
|
||||
c.chip.neutral({ label: ids.length ? `${ids.length} clip${ids.length > 1 ? "s" : ""} selected` : "no clip selected" }).node);
|
||||
};
|
||||
@@ -97,7 +97,7 @@ const zoom = {
|
||||
needs: ["say"],
|
||||
setup({ host, app }) {
|
||||
const b = (label, what, title) => c.button.sm({ label, tone: "ghost", title, onClick: () => app.say(what) }).node;
|
||||
const keys = c.hint.default({ text: "J K L · S split · I O in/out · + − zoom" }).node;
|
||||
const keys = c.hint.default({ text: "J back 1 s · K pause · L play · S split · I / O clip start / end · + − zoom" }).node;
|
||||
keys.classList.add("fp-keyhint"); // the first thing to go when the row runs out of room
|
||||
host.append(keys,
|
||||
b("−", "zoom.out", "Zoom out"), b("+", "zoom.in", "Zoom in"), b("fit", "zoom.fit", "Fit the cut to the window"));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// The guided tour: a walk along the screen, one callout per part, on whatever project is open (it changes nothing).
|
||||
// Help ▸ Welcome tour / Restart tour start it, Exit tour ends it, Skip to FAQ jumps to the last callout.
|
||||
// Help ▸ Start tour / Restart tour start it, Exit tour ends it, Skip to FAQ jumps to the last callout.
|
||||
import { composer as c } from "../../composer/composer.js";
|
||||
|
||||
const zone = (n) => () => document.querySelectorAll("section.cx-zone")[n];
|
||||
@@ -11,7 +11,7 @@ const STEPS = [
|
||||
{ at: zone(2), side: "left", title: "Properties", text: "Project settings (size, length, postfix) and the picked scene's prompt, source, references and length." },
|
||||
{ at: zone(3), side: "top", title: "Timeline", text: "One clip per scene. Drag the edges to trim, press S to split, Space to play. Lanes below carry separated sound." },
|
||||
{ at: button("◆ Composer"), side: "top", title: "Composer", text: "Write the whole story in one box, cut at the cut word; keep templates, $variables and shortcuts; chat with the prompt enhancer." },
|
||||
{ at: button("▶ Generate"), side: "top", title: "Generate", text: "Makes every scene that has no render yet. “Generate this scene” in Properties makes just the picked one; rate a result so FunPack learns your taste." },
|
||||
{ at: button("▶ Generate"), side: "top", title: "Generate", text: "Makes every scene on the timeline, except left-out scenes and video clips; an earlier render of a scene stays as a take. “Generate this scene” in Properties makes just the picked one; rate a result so FunPack learns your taste." },
|
||||
{ at: () => document.querySelector(".cx-frame-main"), side: "bottom", title: "FAQ", text: "Nothing happens? Check the ComfyUI chip in the status bar and the Log window. Wrong look? Settings ▸ Appearance. Lost a layout? View ▸ Reset Layout. Lost a project? File ▸ Open recent. That is the tour: Help ▸ Restart tour runs it again." },
|
||||
];
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// The welcome screen: shown the first time this browser opens the app, and from Help ▸ Welcome tour.
|
||||
// The welcome screen: shown the first time this browser opens the app, and from Help ▸ Welcome window.
|
||||
import { composer as c } from "../../composer/composer.js";
|
||||
import { list } from "../../shell/project.js";
|
||||
|
||||
@@ -38,7 +38,7 @@ export default {
|
||||
const recent = app.project.project;
|
||||
const done = () => { markSeen(); if (win) win.close("done"); };
|
||||
const tiles = c.region.stack({ gap: "sm", children: [
|
||||
card("Update", "Check this install for a newer version", () => app.maintenance.update()), card("Switch branch", "Pick another branch", () => app.maintenance.switchBranch()),
|
||||
card("Update", "Pull the newest version from origin and restart ComfyUI", () => app.maintenance.update()), card("Switch branch", "Pick another branch", () => app.maintenance.switchBranch()),
|
||||
card("Restart ComfyUI", "Reload the server without updating", () => app.maintenance.restart()), card("Rollback update", "Undo the last update", () => app.maintenance.rollback())] });
|
||||
tiles.node.classList.add("fp-tiles");
|
||||
win = c.modal.generic({
|
||||
|
||||
@@ -105,7 +105,9 @@ def h3_reference_to_video():
|
||||
"length": 124, "ref_image_size": "match"}},
|
||||
{"id": "negative", "group": "Preparation", "node": "CLIPTextEncode",
|
||||
# H3 samples at CFG 1, where a negative prompt does nothing -- wired
|
||||
# only because FunPackSampler requires SOME conditioning here.
|
||||
# only because FunPackSampler requires SOME conditioning here. The project's Negative
|
||||
# prompt lands here so Shadow negative and Negative erase have words to work from.
|
||||
"roles": [{"at": "project.negative", "input": "text", "label": "Negative prompt"}],
|
||||
"inputs": {"clip": ["clip", 0], "text": ""}},
|
||||
|
||||
{"id": "settings", "group": "Preparation", "node": "FunPackModifierSettings", "inputs": {"settings": "{}"}},
|
||||
|
||||
@@ -61,8 +61,8 @@ SETTINGS = {
|
||||
},
|
||||
"until_sigma": {
|
||||
"type": "float", "default": 0.6, "min": 0.0, "max": 1.0, "step": 0.05,
|
||||
"label": "How long for", "ui": "slider",
|
||||
"hint": "The starting image comes back sharp once the picture settles.",
|
||||
"label": "Sharp again at noise", "ui": "slider",
|
||||
"hint": "The blurred start is used until the noise falls to this level (1 = full noise, 0 = clean). Lower keeps it blurred for longer; 1 never blurs.",
|
||||
"when": {"enabled": True},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -37,16 +37,21 @@ SETTINGS = {
|
||||
"enabled": {"type": "bool", "default": False, "label": "Context windows (long scenes)",
|
||||
"hint": "Sample a long scene in overlapping windows so it fits the model's memory."},
|
||||
"length": {"type": "int", "default": 145, "min": 9, "max": 2049, "step": 8, "label": "Window length (frames)",
|
||||
"hint": "Frames the model sees at once. Shorter uses less memory; longer keeps motion steadier.",
|
||||
"when": {"enabled": True}},
|
||||
"overlap": {"type": "int", "default": 40, "min": 0, "max": 512, "step": 8, "label": "Window overlap (frames)",
|
||||
"hint": "Frames neighbouring windows share. More overlap joins smoother but runs slower; it must be shorter than the window.",
|
||||
"when": {"enabled": True}},
|
||||
"schedule": {"type": "enum", "default": "standard_uniform", "label": "Window schedule",
|
||||
"hint": "standard_uniform: windows shift a little each step. standard_static: the same windows every step. looped_uniform: the end wraps round to the start. batched: back-to-back windows with no overlap.",
|
||||
"options": [{"value": v, "label": v} for v in SCHEDULES], "when": {"enabled": True}},
|
||||
"fuse": {"type": "enum", "default": "pyramid", "label": "Window blend",
|
||||
"hint": "How overlapping windows are mixed: pyramid, relative, overlap-linear fade between neighbours; flat averages them equally.",
|
||||
"options": [{"value": v, "label": v} for v in FUSES], "when": {"enabled": True}},
|
||||
"freenoise": {"type": "bool", "default": True, "label": "FreeNoise blending", "when": {"enabled": True},
|
||||
"hint": "Shuffle the starting noise so neighbouring windows share it."},
|
||||
"retain_first": {"type": "bool", "default": False, "label": "Pin anchor in every window",
|
||||
"hint": "Keeps the first frame in every window so the whole clip stays tied to your starting image.",
|
||||
"when": {"enabled": True}},
|
||||
}
|
||||
|
||||
|
||||
@@ -47,13 +47,13 @@ SETTINGS = {
|
||||
{"value": "learned", "label": "Learn from ratings"},
|
||||
{"value": "manual", "label": "My value"},
|
||||
],
|
||||
"label": "Value", "ui": "segmented",
|
||||
"hint": "Learn: each rating nudges it toward what you liked.",
|
||||
"label": "How hard", "ui": "segmented",
|
||||
"hint": "Learn from ratings: each rating nudges how much it commits toward what you liked. My value: you set it below.",
|
||||
"when": {"enabled": True},
|
||||
},
|
||||
"k": {
|
||||
"type": "float", "default": 1.0, "min": 0.5, "max": 1.6, "step": 0.05,
|
||||
"label": "k", "ui": "slider",
|
||||
"label": "Commitment (k)", "ui": "slider",
|
||||
"hint": "Above 1 commits harder, below 1 loosens. 1 is off.",
|
||||
"when": {"enabled": True, "mode": "manual"},
|
||||
},
|
||||
|
||||
@@ -31,7 +31,7 @@ SETTINGS = {
|
||||
"enabled": {
|
||||
"type": "bool", "default": False,
|
||||
"label": "Seed search",
|
||||
"hint": "After 10+ ratings, tries a few seeds for one step each and keeps the one your taste likes.",
|
||||
"hint": "After 10+ rated clips (some liked, some disliked), tries a few seeds for one step each and keeps the one your taste likes.",
|
||||
},
|
||||
"candidates": {
|
||||
"type": "int", "default": 3, "min": 2, "max": 8,
|
||||
|
||||
@@ -46,8 +46,8 @@ SETTINGS = {
|
||||
{"value": "learned", "label": "Learn from ratings"},
|
||||
{"value": "manual", "label": "My value"},
|
||||
],
|
||||
"label": "Strength", "ui": "segmented",
|
||||
"hint": "Learn: each rating nudges it toward what you liked.",
|
||||
"label": "How strong", "ui": "segmented",
|
||||
"hint": "Learn from ratings: each rating nudges how much the push is toward what you liked. My value: you set it below.",
|
||||
"when": {"enabled": True},
|
||||
},
|
||||
"strength": {
|
||||
|
||||
@@ -32,7 +32,7 @@ SETTINGS = {
|
||||
"enabled": {
|
||||
"type": "bool", "default": False,
|
||||
"label": "Taste guidance",
|
||||
"hint": "After 10+ ratings, nudges the finish of each clip toward what you rated up.",
|
||||
"hint": "After 10+ rated clips (some liked, some disliked), nudges the finish of each clip toward what you rated up.",
|
||||
},
|
||||
"strength": {
|
||||
"type": "float", "default": 0.02, "min": 0.0, "max": 0.2, "step": 0.005,
|
||||
|
||||
@@ -29,7 +29,7 @@ SETTINGS = {
|
||||
"enabled": {
|
||||
"type": "bool", "default": False,
|
||||
"label": "Taste prompt nudge",
|
||||
"hint": "Shifts the prompt toward what your liked prompts share. Free: no extra passes.",
|
||||
"hint": "Shifts the prompt toward what your liked prompts share. Needs 3 liked clips. Free: no extra passes.",
|
||||
},
|
||||
"strength": {
|
||||
"type": "float", "default": 0.02, "min": 0.0, "max": 0.1, "step": 0.005,
|
||||
|
||||
@@ -30,7 +30,7 @@ SETTINGS = {
|
||||
"enabled": {
|
||||
"type": "bool", "default": False,
|
||||
"label": "Taste attention",
|
||||
"hint": "Learns from your ratings and shifts what the picture pays attention to.",
|
||||
"hint": "Learns from your ratings and shifts what the picture pays attention to. Does nothing until the Taste key has 2+ liked and 2+ disliked runs.",
|
||||
},
|
||||
"strength": {
|
||||
"type": "float", "default": 2.0, "min": 0.0, "max": 10.0, "step": 0.05,
|
||||
|
||||
@@ -35,7 +35,7 @@ SETTINGS = {
|
||||
"enabled": {
|
||||
"type": "bool", "default": False,
|
||||
"label": "Taste slider",
|
||||
"hint": "Leans the finish toward what your liked prompts share. Costs 2 extra passes per late step.",
|
||||
"hint": "Leans the finish toward what your liked prompts share. Needs 3 liked clips. Costs 2 extra passes per late step.",
|
||||
},
|
||||
"strength": {
|
||||
"type": "float", "default": 1.0, "min": -3.0, "max": 3.0, "step": 0.25,
|
||||
|
||||
@@ -45,7 +45,7 @@ SETTINGS = {
|
||||
"enabled": {
|
||||
"type": "bool", "default": False,
|
||||
"label": "Shot memory",
|
||||
"hint": "Starts from the layout of shots you liked, with new details.",
|
||||
"hint": "Some runs start from the layout of a shot you liked, with new details; ratings tune how often.",
|
||||
},
|
||||
"mode": {
|
||||
"type": "enum", "default": "learned", "options": [
|
||||
@@ -53,7 +53,7 @@ SETTINGS = {
|
||||
{"value": "manual", "label": "My value"},
|
||||
],
|
||||
"label": "How much", "ui": "segmented",
|
||||
"hint": "Learn: how strongly to reuse is tuned by your ratings.",
|
||||
"hint": "Learn from ratings: each rating nudges how much is reused toward what you liked. My value: you set it below.",
|
||||
"when": {"enabled": True},
|
||||
},
|
||||
"amount": {
|
||||
|
||||
@@ -51,14 +51,14 @@ SETTINGS = {
|
||||
{"value": "learned", "label": "Learn from ratings"},
|
||||
{"value": "manual", "label": "My value"},
|
||||
],
|
||||
"label": "Strength", "ui": "segmented",
|
||||
"hint": "Learn: each rating nudges it toward what you liked.",
|
||||
"label": "How strong", "ui": "segmented",
|
||||
"hint": "Learn from ratings: each rating nudges how much the boost is toward what you liked. My value: you set it below.",
|
||||
"when": {"enabled": True},
|
||||
},
|
||||
"alpha": {
|
||||
"type": "float", "default": 2.0, "min": 0.5, "max": 3.0, "step": 0.1,
|
||||
"label": "Alpha", "ui": "slider",
|
||||
"hint": "The paper used 1.2-2.5.",
|
||||
"hint": "Higher boosts more. The paper used 1.2-2.5.",
|
||||
"when": {"enabled": True, "mode": "manual"},
|
||||
},
|
||||
"block": {
|
||||
|
||||
@@ -27,8 +27,8 @@ SETTINGS = {
|
||||
"style": {
|
||||
"type": "enum", "default": "natural", "label": "Temporal style",
|
||||
"options": [{"value": v, "label": v.replace("_", " ")} for v in STYLES],
|
||||
"hint": "How motion and time feel. accelerate/freeze hold the motion, decelerate/rapid make it punchier, "
|
||||
"pulse repeats ease-downs, loop joins the end to the start.",
|
||||
"hint": "How motion feels. natural: unchanged. accelerate, freeze: smoother, held motion (freeze most). decelerate: punchier, bigger steps. "
|
||||
"pulse: repeated punch-then-ease. rapid start/end: punchy at the start and/or end. loop: the end joins the start.",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ SETTINGS = {
|
||||
"enabled": {
|
||||
"type": "bool", "default": False,
|
||||
"label": "Early taste guidance",
|
||||
"hint": "After 10+ ratings, nudges motion and layout too, not just the finish.",
|
||||
"hint": "After 10+ rated clips (some liked, some disliked), nudges motion and layout too, not just the finish.",
|
||||
},
|
||||
"strength": {
|
||||
"type": "float", "default": 0.02, "min": 0.0, "max": 0.2, "step": 0.005,
|
||||
|
||||
@@ -7,7 +7,7 @@ latent is turned toward the average banked direction, without adding energy (the
|
||||
with a cap so it never wipes the current clip. A thin overlay of remembered motion: the best use is
|
||||
crossing a remembered action onto a new prompt, and it can re-insert a cut your liked clips had.
|
||||
|
||||
"Like the nearest liked prompt" picks one real clip's motion instead of the average of all.
|
||||
"Like the nearest liked prompt" picks one real clip's motion instead of the average of all (the average when none is close enough).
|
||||
Banked directions are the size of the whole latent, so a clip is only matched with banked clips of the
|
||||
same shape (length and frame size); a run of another shape says so and goes unbiased.
|
||||
Picture only: sound is never touched.
|
||||
|
||||
Reference in New Issue
Block a user