Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
38 lines
3.3 KiB
JavaScript
38 lines
3.3 KiB
JavaScript
// Shot camera memory: the words and views the camera has learned from your prompts and ratings, to read and forget.
|
|
import { composer as c } from "../../composer/composer.js";
|
|
|
|
export const shotMemory = (app) => function mount() {
|
|
const page = c.region.stack({ gap: "md" });
|
|
let data = null, error = "";
|
|
|
|
async function refresh() {
|
|
try { data = await app.api.shotMemory(); error = ""; } catch (err) { data = null; error = err.message; }
|
|
draw();
|
|
}
|
|
async function forget(kind, name) {
|
|
const what = kind === "all" ? "everything the camera has learned" : `“${name}”`;
|
|
if (!(await c.modal.dialogue({ title: "Forget", message: `Forget ${what}? This cannot be undone.`, tone: "danger", confirmLabel: "Forget" }).result)) return;
|
|
try { await app.api.forgetShotMemory(kind, name); } catch (err) { c.toast.warn({ text: err.message }); }
|
|
refresh();
|
|
}
|
|
const row = (label, hint, kind, name) => c.settingsRow.default({ label, hint, control: c.button.sm({ label: "Forget", tone: "danger", onClick: () => forget(kind, name) }) });
|
|
function draw() {
|
|
if (error) return page.set([c.banner.warn({ text: `Could not read the camera's memory: ${error}` })]);
|
|
if (!data) return page.set([c.hint.default({ text: "Loading…" })]);
|
|
const words = data.words || [], views = data.views || [];
|
|
const details = data.details || [];
|
|
const infusion = data.infusion || [];
|
|
if (!words.length && !views.length && !(data.arms || []).length && !details.length && !infusion.length) return page.set([c.emptyState.default({ icon: "◎", title: "Nothing learned yet", hint: "In a pipeline with the Shot camera node, turn on camera moves, shot views or liked details in Engine, then rate renders: the camera remembers which words, views and details you liked." })]);
|
|
page.set([c.hint.default({ text: `${data.prompts} prompts seen · ${data.shots} shots reviewed. Forget anything that taught it the wrong thing.` }),
|
|
...(details.length ? [c.label.section({ text: "Liked details" }), ...details.map((d) => row(d.phrase, `${d.kept ? "will be added" : "dropped"} · liked ${d.good.toFixed(1)} · disliked ${d.bad.toFixed(1)}`, "detail", d.phrase))] : []),
|
|
...(views.length ? [c.label.section({ text: "Views" }), ...views.map((v) => row(v.view, `liked ${v.good.toFixed(1)} · disliked ${v.bad.toFixed(1)}`, "view", v.view))] : []),
|
|
...(words.length ? [c.label.section({ text: "Words the camera aims at" }), ...words.map((w) => row(w.word, `picked ${w.picks} · replaced ${w.rejects} · seen ${w.seen}`, "word", w.word)),
|
|
data.more ? c.hint.default({ text: `and ${data.more} more` }) : null] : []),
|
|
...(infusion.length ? [c.label.section({ text: "Subjects the camera may infuse" }), ...infusion.map((w) => row(w.word, `in ${w.seen} prompts · liked ${w.good.toFixed(1)} · disliked ${w.bad.toFixed(1)} · weight ${w.weight}`, "word", w.word))] : []),
|
|
...((data.arms || []).length ? [c.label.section({ text: "Camera choices" }), ...data.arms.map((a) => row(a.arm.replace(":", " · "), `liked ${a.good.toFixed(1)} · disliked ${a.bad.toFixed(1)}`, "arm", a.arm))] : []),
|
|
c.button.sm({ label: "Forget everything", tone: "danger", onClick: () => forget("all") })].filter(Boolean));
|
|
}
|
|
draw(); refresh();
|
|
return { node: page.node, destroy: () => page.node.remove() };
|
|
};
|