feat: Pin reference to slot RN in a reference's tile menu, beside Stop using as a reference
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
6634dd08e6
commit
03df8882f6
@@ -63,3 +63,26 @@ test("a changed pin repaints the slot numbers on the tiles even when the list of
|
||||
fire("project");
|
||||
assert.deepEqual([flag("cat.png"), flag("dog.png")], ["R1", "R2"], "unpinned: back to list order");
|
||||
});
|
||||
|
||||
test("a reference's tile menu pins it at the slot number it holds, for every scene, and takes the pin off again", async () => {
|
||||
const doc = { fixed_references: {} };
|
||||
const project = { selected: { id: "s1", references: ["m1", "m2"] }, project: doc, setScene: () => {}, edit: (fn) => fn(doc) };
|
||||
const api = { media: async () => ({ media: [{ id: "m1", name: "cat.png", kind: "image" }, { id: "m2", name: "dog.png", kind: "image" }, { id: "m3", name: "owl.png", kind: "image" }] }) };
|
||||
const host = document.createElement("div");
|
||||
hub.setup({ host, app: { project, api, mediaPeek: {}, say: () => {}, on: () => () => {} } });
|
||||
await new Promise((r) => setTimeout(r, 10));
|
||||
const named = (n) => [...host.querySelectorAll(".cx-cell")].find((x) => x.textContent.includes(n));
|
||||
const menuFor = (n) => {
|
||||
document.querySelectorAll(".cx-popover").forEach((x) => x.remove());
|
||||
named(n).dispatchEvent(new window.MouseEvent("contextmenu", { bubbles: true, cancelable: true }));
|
||||
return [...document.querySelectorAll(".cx-menu-item")].map((b) => b.textContent);
|
||||
};
|
||||
assert.ok(menuFor("dog.png").includes("Pin reference to slot R2"));
|
||||
assert.ok(!menuFor("owl.png").some((t) => t.includes("slot R")), "a picture that is no reference has nothing to pin");
|
||||
menuFor("dog.png");
|
||||
[...document.querySelectorAll(".cx-menu-item")].find((b) => b.textContent === "Pin reference to slot R2").click();
|
||||
assert.deepEqual(doc.fixed_references, { m2: 2 });
|
||||
assert.ok(menuFor("dog.png").includes("Unpin reference from slot R2"));
|
||||
[...document.querySelectorAll(".cx-menu-item")].find((b) => b.textContent === "Unpin reference from slot R2").click();
|
||||
assert.deepEqual(doc.fixed_references, {});
|
||||
});
|
||||
|
||||
@@ -32,12 +32,20 @@ export default {
|
||||
p.setScene(sc.id, "source_image", item.id);
|
||||
draw();
|
||||
};
|
||||
/** The slot number this picture holds in the selected scene, and whether it is pinned there: null when it is not one of its references. */
|
||||
const slotOf = (id) => {
|
||||
const sc = p.selected;
|
||||
if (!sc) return null;
|
||||
const plan = sceneReferences(sc, (p.project || {}).fixed_references), i = plan.ids.indexOf(id);
|
||||
return i < 0 ? null : { n: referenceNumbers(plan.ids, plan.pins)[i], pinned: id in plan.pins };
|
||||
};
|
||||
const pinItem = (it) => { const s = slotOf(it.id); return s ? [{ id: "pinref", label: s.pinned ? `Unpin reference from slot R${s.n}` : `Pin reference to slot R${s.n}` }] : []; };
|
||||
const extras = (it) => (app.mediaMenu || []).flatMap((f) => { try { return f(it, items.find((m) => m.id === it.id)) || []; } catch { return []; } }); // entries other features add to a tile's menu: { id, label, run }
|
||||
const context = (it, e) => c.menu.context({ x: e.clientX, y: e.clientY, onPick: (id) => (act[id] ? act[id](it) : extras(it).find((x) => x.id === id)?.run()),
|
||||
items: [{ id: "look", label: "Look at it on the monitor" }, ...(p.selected && p.selected.source_image === it.id ? [{ id: "unres", label: "Stop using as the selected scene's resolution source" }]
|
||||
: [{ id: "res", label: "Use as the selected scene's resolution source", disabled: !p.selected || it.kind === "audio" || !items.some((m) => m.id === it.id && m.kind === "image") }]),
|
||||
p.selected && (p.selected.references || []).includes(it.id) ? { id: "unref", label: "Stop using as a reference for the selected scene" }
|
||||
: { id: "ref", label: "Use as a reference for the selected scene", disabled: !p.selected }, { id: "rename", label: "Rename…" },
|
||||
: { id: "ref", label: "Use as a reference for the selected scene", disabled: !p.selected }, ...pinItem(it), { id: "rename", label: "Rename…" },
|
||||
{ id: "export", label: "Save to your computer", disabled: !items.some((m) => m.id === it.id && m.kind !== "audio") }, ...extras(it).map(({ id, label, disabled }) => ({ id, label, disabled })), { separator: true }, { id: "delete", label: "Delete from the bin", danger: true }] });
|
||||
/** Delete these from the bin, and nothing in this project may keep pointing at them. */
|
||||
async function gone(ids) {
|
||||
@@ -59,6 +67,10 @@ export default {
|
||||
const act = {
|
||||
look: (it) => peek(it), res: (it) => pick(it),
|
||||
ref: (it) => p.setScene(p.selected.id, "references", [...(p.selected.references || []), it.id]),
|
||||
pinref: (it) => { // pin at the number it holds now, for every scene, or take the pin off
|
||||
const s = slotOf(it.id);
|
||||
if (s) p.edit((pr) => { const next = { ...(pr.fixed_references || {}) }; if (s.pinned) delete next[it.id]; else next[it.id] = s.n; pr.fixed_references = next; return true; });
|
||||
},
|
||||
unref: (it) => p.setScene(p.selected.id, "references", (p.selected.references || []).filter((r) => r !== it.id)),
|
||||
unres: () => p.setScene(p.selected.id, "source_image", ""),
|
||||
rename: async (it) => {
|
||||
|
||||
Reference in New Issue
Block a user