A 'what to do next' guide, and the picture checker's findings with a Fix button

The page never said what the next step was, and finding faulty pictures was
left to the author although every frame is already checked by a model.
This commit is contained in:
gator 2026-09-19 16:11:11 +00:00
parent 39c41c689d
commit 3d34667b35

View file

@ -50,6 +50,23 @@
.whoin .p input{width:100%;box-sizing:border-box;font:inherit;padding:5px 7px;
border:1px solid var(--line);border-radius:6px;background:transparent;color:inherit}
.whoin .p .go{margin-top:5px;font-size:12px;padding:3px 9px}
.guide{border:1px solid var(--line);border-radius:12px;padding:14px 16px;margin:0 0 18px}
.guide h4{margin:0 0 10px;font-size:15px}
.guide ol{list-style:none;margin:0;padding:0;display:grid;gap:7px}
.guide li{display:flex;gap:10px;align-items:baseline;font-size:13px;color:var(--muted)}
.guide li .n{flex:none;width:20px;height:20px;border-radius:50%;border:1px solid var(--line);
display:inline-flex;align-items:center;justify-content:center;font-size:11px}
.guide li.done .n{background:#1f7a3a;border-color:#1f7a3a;color:#fff}
.guide li.now{color:inherit;font-weight:600}
.guide li.now .n{background:#4B8BF5;border-color:#4B8BF5;color:#fff}
.guide li .d{font-weight:400;color:var(--muted);display:block;margin-top:2px}
.guide li .btn{margin-left:4px}
.fchk{font-size:12px;margin:0 0 10px;padding:7px 10px;border-radius:8px;border:1px solid var(--line)}
.fchk.ok{color:#1f7a3a;border-color:transparent;padding-left:0}
.fchk.bad{border-color:#b4531f}
.fchk .w{display:block;margin:3px 0}
.fchk .w.serious{color:#b4531f;font-weight:600}
.fchk .go{font-size:12px;padding:2px 9px;margin-left:6px}
.ffault{display:flex;gap:8px;margin:0 0 10px}
.ffault input{flex:1;min-width:0;font:inherit;font-size:12px;padding:5px 8px;
border:1px solid var(--line);border-radius:6px;background:transparent;color:inherit}
@ -224,7 +241,12 @@
return (Date.now() - t) < 25 * 60 * 1000;
}
var DRAFTNOW = null;
function busyShots() {
var ps = String(PKG.pics_state || "");
if (ps && ps !== "ready"
&& (Date.now() - (Date.parse(PKG.pics_started || "") || 0)) < 15 * 60 * 1000) return true;
if (String(PKG.look_state || "")
&& (Date.now() - (Date.parse(PKG.look_started || "") || 0)) < 15 * 60 * 1000) return true;
return (PKG.shots || []).some(function (s) {
@ -821,7 +843,8 @@
var body = insertHtml("start");
shots.forEach(function (s) { body += shotHtml(s, voice) + insertHtml(s.id); });
return head + probs + '<div class="bar">' + barHtml(meta) + '</div>'
DRAFTNOW = draft;
return head + guideHtml(meta) + probs + '<div class="bar">' + barHtml(meta) + '</div>'
+ '<div id="reelhost">' + reelHtml() + '</div>'
+ spineHtml() + cutsHtml(meta) + reviewsHtml(meta) + wholeHtml() + castHtml + body;
}
@ -849,6 +872,103 @@
}).join("");
}
// WHAT TO DO NEXT. The steps of making a trailer, in order, with the one
// button that matters now. Everything else on the page is still there for
// anyone who wants it; nobody has to understand it to get a film made.
function guideHtml(meta) {
var shots = PKG.shots || [];
if (!shots.length || meta.writer !== RUN.lead_writer) return "";
var ds = String(meta.draw_state || "");
var drawing = ds && !/^(drawn|failed)/.test(ds);
var toDraw = Number(PKG.pictures_to_draw || 0);
var drawn = shots.filter(function (x) { return x.opening_url; });
var unchecked = drawn.filter(function (x) {
return !x.clip_url && !((x.frame_check || {}).verdict); }).length;
var faulty = drawn.filter(function (x) {
return !x.clip_url && (x.frame_check || {}).verdict === "broken"; });
var ps = String(PKG.pics_state || "");
var checking = ps && ps !== "ready";
var reelReady = String(PKG.reel_state || "") === "ready";
var unfilmed = shots.filter(function (x) { return !x.clip_url; }).length;
var objections = String((DRAFTNOW || {}).problems || "").split("\n").filter(Boolean).length;
var price = function (n) { return "$" + n.toFixed(2); };
var steps = [];
steps.push({ t: "Read the script", done: true,
d: objections ? "The checker has " + objections + " objection" + (objections === 1 ? "" : "s")
+ " - they are listed just below." : "" });
steps.push({ t: "Tell the writer what to change", done: true, optional: true,
d: "Write in the box \u201cYour notes on the script as a whole\u201d, or under any shot, then press "
+ "\u201cRevise the whole script\u201d. Skip this if you are happy." });
var s3 = { t: "Draw the pictures", done: toDraw === 0 && drawn.length > 0 };
if (drawing) s3.d = "Drawing now\u2026 " + esc(ds) + ". You can leave this page open.";
else if (!s3.done) s3.btn = '<button type="button" class="btn primary" data-draw="' + esc(meta.id)
+ '">Draw ' + toDraw + ' picture' + (toDraw === 1 ? '' : 's') + ' \u00b7 ' + price(toDraw * 0.068) + '</button>';
steps.push(s3);
var s4 = { t: "Check the pictures", done: s3.done && !unchecked && !faulty.length };
if (checking) s4.d = "Looking at every picture for mistakes\u2026 " + esc(ps);
else if (s3.done && unchecked) {
s4.d = "A second model looks at every picture for the mistakes these tools make - the wrong time "
+ "of day, the wrong clothes, extra people, six fingers - and redraws any that are broken.";
s4.btn = '<button type="button" class="btn primary" data-chkpics="1">Check ' + unchecked
+ ' picture' + (unchecked === 1 ? '' : 's') + ' \u00b7 ' + price(unchecked * 0.02) + '</button>';
} else if (s3.done && faulty.length)
s4.d = faulty.length + " picture" + (faulty.length === 1 ? " still has" : "s still have")
+ " a problem: shot" + (faulty.length === 1 ? " " : "s ")
+ faulty.map(function (x) { return x.id; }).join(", ")
+ ". Scroll to " + (faulty.length === 1 ? "it" : "them") + " and press \u201cFix this\u201d, "
+ "or carry on if you think the checker is being fussy.";
steps.push(s4);
var s5 = { t: "Listen to the storyboard", done: reelReady };
if (s3.done && !reelReady)
s5.btn = '<button type="button" class="btn' + (s4.done ? ' primary' : '')
+ '" data-reelgo="1">Play it with the voices</button>';
steps.push(s5);
var s6 = { t: "Film it", done: unfilmed === 0 };
if (s3.done && unfilmed)
s6.btn = '<button type="button" class="btn' + (s4.done && reelReady ? ' primary' : '')
+ '" data-film="' + esc(meta.id) + '">Film ' + unfilmed + ' shot' + (unfilmed === 1 ? '' : 's')
+ ' \u00b7 ' + price(unfilmed * 0.75) + '</button>';
steps.push(s6);
var nowSet = false;
return '<div class="guide"><h4>What to do next</h4><ol>' + steps.map(function (st, i) {
var isNow = !nowSet && !st.done && !st.optional;
if (isNow) nowSet = true;
return '<li class="' + (st.done ? 'done' : '') + (isNow ? ' now' : '') + '">'
+ '<span class="n">' + (st.done && !st.optional ? '\u2713' : (i + 1)) + '</span>'
+ '<span>' + esc(st.t) + (st.optional ? ' <i>(if you want)</i>' : '')
+ ((isNow || (st.btn && /primary/.test(st.btn))) && st.btn ? ' ' + st.btn : '')
+ (st.d && (isNow || st.optional || i === 0) ? '<span class="d">' + st.d + '</span>' : '')
+ '</span></li>';
}).join("") + '</ol></div>';
}
// What the checker saw in this shot's opening picture, in its own sentence.
function frameCheckHtml(s) {
var fc = s.frame_check || {};
if (!s.opening_url || s.clip_url || !fc.verdict || s.fixing || s.drawing) return "";
var faults = (fc.faults || []).filter(function (f) { return f && f.what; });
var dup = (fc.duplicated || []).filter(Boolean);
var retried = s.frame_retried ? ' <i>(' + esc(s.frame_retried) + ')</i>' : '';
if (!faults.length && !dup.length)
return '<div class="fchk ok">\u2713 picture checked - nothing wrong' + retried + '</div>';
var rows = dup.map(function (d) {
return { what: "The same person appears twice: " + d, serious: true }; })
.concat(faults.map(function (f) { return { what: f.what, serious: f.severity === "serious" }; }));
return '<div class="fchk ' + (fc.verdict === "broken" ? 'bad' : '') + '">'
+ '<b>The checker looked at this picture and sees:</b>' + retried
+ rows.map(function (r, i) {
return '<span class="w' + (r.serious ? ' serious' : '') + '">' + esc(r.what)
+ ' <button type="button" class="btn go" data-fixfault="' + esc(s.id) + '|' + i + '">Fix this</button></span>';
}).join("")
+ '</div>';
}
// "That is not right" - about the PICTURE, in the author's own words. The
// picture is what the shot is filmed from, so a fault here is inherited by
// the film and cannot be fixed by filming again.
@ -1037,6 +1157,7 @@
+ (s.day ? ' &middot; <span class="scene">' + esc(s.day) + '</span>' : '')
+ '</div>'
+ media
+ frameCheckHtml(s)
+ frameFaultHtml(s)
+ whoInHtml(s)
+ '<p class="act">' + esc(s.action || "") + '</p>'
@ -1225,6 +1346,28 @@
z.classList.add("on");
return;
}
if ((el = hit("data-chkpics"))) {
el.disabled = true; el.textContent = "starting\u2026";
r = await act("check_pictures", { draft_id: CUR });
if (r) { showMsg("msg", r.shots ? "Checking " + r.shots + " pictures. Broken ones are redrawn "
+ "automatically. This takes a few minutes." : (r.note || "Already checked."), "ok"); await refresh(); }
return;
}
if ((el = hit("data-fixfault"))) {
var ff = el.getAttribute("data-fixfault").split("|");
var fs = (PKG.shots || []).filter(function (x) { return String(x.id) === ff[0]; })[0] || {};
var fcx = fs.frame_check || {};
var all = (fcx.duplicated || []).filter(Boolean).map(function (d) {
return "The same person appears twice: " + d + ". Each person appears once only."; })
.concat((fcx.faults || []).filter(function (f) { return f && f.what; })
.map(function (f) { return f.what; }));
var say = all[Number(ff[1])] || "";
if (!say) return;
el.disabled = true; el.textContent = "working\u2026";
r = await act("report_frame_fault", { draft_id: CUR, shot_id: ff[0], note: say });
if (r) { showMsg("msg", "Redrawing shot " + ff[0] + " to fix that. About a minute.", "ok"); await refresh(); }
return;
}
if ((el = hit("data-ffgo"))) {
var fsid = el.getAttribute("data-ffgo");
var fbox = document.getElementById("ff-" + fsid);