Mirrors mediabrowser.js's card pin button in the Editor: a 📌 button on
each image thumbnail (chain-sampler pipelines only) toggles
continuity_settings.identity_pin_ref, re-enabling auto_enabled if it was
off (a pin does nothing with auto continuity disabled). Same setting as
Engine settings > Continuity > Identity pin, just reachable without
leaving the Gallery. Verified both surfaces read/write the same
continuity_settings and stay in sync.
117 lines
4.1 KiB
JavaScript
117 lines
4.1 KiB
JavaScript
// "Gallery" picker: choose an already-uploaded media-bin file as the i2v anchor,
|
|
// without re-uploading it. Standalone modal (same modal-overlay/.modal shell as
|
|
// project_menu.js) since Easy Gen has no Media Bin panel of its own.
|
|
(function () {
|
|
const { el, clear } = window.dom;
|
|
const API = window.MovieEditorAPI;
|
|
const S = window.Store;
|
|
let overlay = null;
|
|
|
|
function close() {
|
|
if (overlay) { overlay.remove(); overlay = null; }
|
|
}
|
|
|
|
// Continuity pin, right on the gallery card — same setting as Engine settings ▸
|
|
// Continuity ▸ Identity pin, just quicker to reach (mirrors mediabrowser.js's card
|
|
// pin button in the Editor). Images only, chain-sampler pipelines only.
|
|
const pinEls = []; // { id, btn, badge } for every image cell in the currently-open grid
|
|
|
|
function refreshPins() {
|
|
const cs = (S.get().project || {}).continuity_settings || {};
|
|
pinEls.forEach(({ id, btn, badge }) => {
|
|
const isPin = cs.identity_pin_ref === id;
|
|
btn.classList.toggle("active", isPin);
|
|
btn.title = isPin
|
|
? "Continuity pin — click to unpin"
|
|
: "Pin as identity guide for every scene (continuity pin)"
|
|
+ (cs.auto_enabled === false ? " — also turns Auto continuity back on" : "");
|
|
badge.hidden = !isPin;
|
|
});
|
|
}
|
|
|
|
function togglePin(m) {
|
|
const cs = (S.get().project || {}).continuity_settings || {};
|
|
const isPin = cs.identity_pin_ref === m.id;
|
|
const patch = { ...cs, identity_pin_ref: isPin ? null : m.id };
|
|
if (!isPin && patch.auto_enabled === false) patch.auto_enabled = true;
|
|
S.patchProject({ continuity_settings: patch });
|
|
refreshPins();
|
|
}
|
|
|
|
function cellFor(m) {
|
|
const cell = el("div", "gal-cell");
|
|
cell.title = m.name || m.id;
|
|
const thumb = el("div", "gal-thumb");
|
|
const url = API.mediaUrl(m.id);
|
|
if (m.kind === "video") {
|
|
const v = el("video");
|
|
v.src = url; v.muted = true; v.preload = "metadata";
|
|
thumb.append(v);
|
|
} else {
|
|
const img = el("img");
|
|
img.src = url; img.loading = "lazy";
|
|
thumb.append(img);
|
|
}
|
|
if (m.kind === "image" && window.PipelineCaps?.usesChainSampler(S.get())) {
|
|
const btn = el("button", "gal-pin-btn", "📌");
|
|
btn.type = "button";
|
|
btn.onclick = (e) => { e.stopPropagation(); togglePin(m); };
|
|
thumb.append(btn);
|
|
const badge = el("span", "gal-pin-badge", "📌");
|
|
badge.hidden = true;
|
|
thumb.append(badge);
|
|
pinEls.push({ id: m.id, btn, badge });
|
|
}
|
|
cell.append(thumb);
|
|
cell.append(el("div", "gal-name", m.name || m.id));
|
|
cell.onclick = async () => {
|
|
S.setSceneMedia(m.id, m.kind === "video" ? "video" : "image");
|
|
await S.save();
|
|
close();
|
|
};
|
|
return cell;
|
|
}
|
|
|
|
async function open() {
|
|
if (!S.get().project) return;
|
|
close();
|
|
pinEls.length = 0;
|
|
overlay = el("div", "modal-overlay");
|
|
const box = el("div", "modal modal-wide");
|
|
const head = el("div", "modal-head");
|
|
head.append(el("div", "modal-title", "Gallery — choose an anchor"));
|
|
const x = el("button", "btn ghost tiny", "✕");
|
|
x.onclick = close;
|
|
head.append(el("div", "modal-head-right"), x);
|
|
box.append(head);
|
|
|
|
const content = el("div", "modal-content gal-grid");
|
|
content.append(el("div", "sw-hint", "Loading…"));
|
|
box.append(content);
|
|
|
|
overlay.append(box);
|
|
overlay.addEventListener("click", (e) => { if (e.target === overlay) close(); });
|
|
document.addEventListener("keydown", function onKey(e) {
|
|
if (e.key === "Escape") { close(); document.removeEventListener("keydown", onKey); }
|
|
});
|
|
document.body.append(overlay);
|
|
|
|
try {
|
|
const r = await API.listMedia();
|
|
const items = (r.media || []).filter((m) => m.kind === "image" || m.kind === "video");
|
|
clear(content);
|
|
if (!items.length) {
|
|
content.append(el("div", "sw-hint", "No uploaded media yet — use Upload first."));
|
|
return;
|
|
}
|
|
items.forEach((m) => content.append(cellFor(m)));
|
|
refreshPins();
|
|
} catch (e) {
|
|
clear(content);
|
|
content.append(el("div", "sw-hint", "Could not load media: " + (e.message || e)));
|
|
}
|
|
}
|
|
|
|
window.EasyGallery = { open, close };
|
|
})();
|