feat(megaprompt-v4): on-screen custom text input and frozen prompt editor

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Isulion
2026-10-20 20:43:57 +02:00
co-authored by Claude Opus 5.5
parent 6b97a20329
commit 1fbddff39a
2 changed files with 95 additions and 3 deletions
+30
View File
@@ -114,6 +114,36 @@ test("removing the node twice is safe and leaves nothing running", async () => {
const mine = fake.observers.slice(obsBefore); assert.ok(mine.length >= 1 && mine.every((o) => o.disconnected));
});
test("inline edit pins a custom subject and rerolls that reel", async () => {
const node = await createNode(); api.responders["/isulion/megaprompt_v4/roll"] = payload();
const j = node._jackpot; click(j.ui.reels.subject.btns.edit);
assert.equal(j.ui.editor.panel.hidden, false); j.ui.editor.input.value = " my elf queen ";
j.ui.editor.input.dispatchEvent(new KeyboardEvent("keydown", { key: "Enter" })); await tick();
assert.equal(lastRoll().body.state.reels.subject.custom, "my elf queen"); assert.deepEqual(lastRoll().body.reels, ["subject"]);
assert.equal(j.ui.editor.panel.hidden, true);
});
test("escape cancels the inline edit", async () => {
const node = await createNode(); const before = api.calls.length; const j = node._jackpot;
click(j.ui.reels.location.btns.edit); j.ui.editor.input.value = "x";
j.ui.editor.input.dispatchEvent(new KeyboardEvent("keydown", { key: "Escape" }));
assert.equal(j.state.reels.location.custom, ""); assert.equal(api.calls.length, before); assert.equal(j.ui.editor.panel.hidden, true);
});
test("clear button removes the custom text and rerolls", async () => {
const node = await createNode(); api.responders["/isulion/megaprompt_v4/roll"] = payload(); const j = node._jackpot;
j.state.reels.location.custom = "my castle"; click(j.ui.reels.location.btns.edit);
assert.equal(j.ui.editor.input.value, "my castle"); click(j.ui.editor.clear); await tick();
assert.equal(lastRoll().body.state.reels.location.custom, ""); assert.deepEqual(lastRoll().body.reels, ["location"]);
});
test("edit prompt freezes it and the lever unfreezes", async () => {
const node = await createNode(); api.responders["/isulion/megaprompt_v4/roll"] = payload({ rolled: [], segments: [{ reel: "frozen", text: "fixed text" }], prompt: "fixed text", state: { ...readState(null), frozen_prompt: "fixed text" } });
const j = node._jackpot; click(j.ui.editBtn); j.ui.editor.textarea.value = "fixed text";
click(j.ui.editor.ok); await tick();
assert.equal(lastRoll().body.state.frozen_prompt, "fixed text"); assert.deepEqual(lastRoll().body.reels, []); await tick();
assert.equal(j.ui.frozenBadge.hidden, false);
click(j.ui.lever); await tick();
assert.equal(lastRoll().body.state.frozen_prompt, ""); assert.deepEqual(lastRoll().body.reels, REELS);
});
// --- runner (keep last) ---------------------------------------------------
let failures = 0;
for (const [name, fn] of tests) {
+65 -3
View File
@@ -278,6 +278,17 @@ class PromptJackpot {
const ok = el("button");
row.append(clear, cancel, ok);
panel.append(input, textarea, row);
const onKey = (e) => {
e.stopPropagation(); // Delete/Backspace must not reach LiteGraph while typing
if (e.key === "Escape") this.closeEditor();
if (e.key === "Enter" && e.target === input) this.confirmEditor();
};
input.addEventListener("keydown", onKey);
textarea.addEventListener("keydown", onKey);
ok.addEventListener("click", () => this.confirmEditor());
cancel.addEventListener("click", () => this.closeEditor());
clear.addEventListener("click", () => this.clearCustom());
panel.addEventListener("pointerdown", (e) => { if (e.target === panel) this.closeEditor(); });
this.ui.editor = { panel, input, textarea, clear, cancel, ok };
return panel;
}
@@ -481,9 +492,60 @@ class PromptJackpot {
this.ui.search.list.replaceChildren(...rows);
}
// inline editors (Tâche 9)
openInlineEdit() {}
openPromptEditor() {}
// on-screen editors: custom subject/location text, or the whole frozen prompt
openInlineEdit(reel) {
const { panel, input, textarea, clear } = this.ui.editor;
this.editing = { reel };
input.hidden = false;
clear.hidden = false;
textarea.hidden = true;
input.value = this.state.reels[reel].custom;
panel.hidden = false;
input.focus();
}
openPromptEditor() {
const { panel, input, textarea, clear } = this.ui.editor;
this.editing = { prompt: true };
input.hidden = true;
clear.hidden = true;
textarea.hidden = false;
textarea.value = this.state.frozen_prompt || (this.view.last?.prompt ?? "");
panel.hidden = false;
textarea.focus();
}
closeEditor() {
this.editing = null;
this.ui.editor.panel.hidden = true;
}
confirmEditor() {
const editing = this.editing;
if (!editing) return;
this.closeEditor();
if (editing.prompt) {
this.state.frozen_prompt = this.ui.editor.textarea.value;
this.commit();
this.roll([]);
} else {
this.setCustom(editing.reel, this.ui.editor.input.value.trim());
}
}
clearCustom() {
const editing = this.editing;
if (!editing?.reel) return;
this.closeEditor();
this.setCustom(editing.reel, "");
}
setCustom(reel, text) {
this.state.reels[reel].custom = text;
this.commit();
this.renderReel(reel);
this.roll([reel]);
}
// history (Tâche 10)
pushHistory() {}