diff --git a/trailer.html b/trailer.html index bcff926..c49cfea 100644 --- a/trailer.html +++ b/trailer.html @@ -14,7 +14,6 @@ .tab.lead{color:var(--accent)} .tab .sc{color:var(--accent2);margin-left:7px;font-weight:600} .tab .st{color:var(--muted);margin-left:7px;font-size:12px} - .tab .crown{margin-left:6px} .head{display:flex;gap:18px;flex-wrap:wrap;align-items:baseline;margin-bottom:6px} .head h2{margin:0;font-size:22px} .tag{color:var(--muted);font-style:italic} @@ -28,7 +27,7 @@ .sheets img{width:33%;border-radius:6px;border:1px solid var(--line);display:block; aspect-ratio:16/9;object-fit:cover} .shot{border-top:1px solid var(--line);padding:16px 0;display:flex;gap:16px} - .shot .num{width:34px;flex:none;color:var(--muted);font-variant-numeric:tabular-nums} + .shot .num{width:40px;flex:none;color:var(--muted);font-variant-numeric:tabular-nums} .shot .body{flex:1;min-width:0} .shot h3{margin:0 0 3px;font-size:15px} .shot .where{color:var(--muted);font-size:12px;margin-bottom:8px} @@ -36,7 +35,7 @@ .shot .cam,.shot .cont{color:var(--muted);font-size:12.5px;margin:0 0 5px} .frames{display:flex;gap:10px;margin:10px 0} .frames figure{margin:0;flex:1;min-width:0} - .frames img{width:100%;border-radius:8px;border:1px solid var(--line);display:block} + .frames img,.frames video{width:100%;border-radius:8px;border:1px solid var(--line);display:block} .frames figcaption{color:var(--muted);font-size:11px;margin-top:3px} .line{display:flex;gap:9px;align-items:flex-start;margin:7px 0} .line .say{flex:1;min-width:0;font-size:14px} @@ -65,11 +64,21 @@ .rev{border:1px solid var(--line);border-radius:10px;padding:12px 14px;margin-bottom:10px} .rev h4{margin:0 0 6px;font-size:14px} .rev h4 .n{color:var(--accent2);font-weight:600;margin-left:8px} - .rev .v{color:var(--muted);font-size:13px;margin:0 0 8px} .rev ul{margin:4px 0 8px;padding-left:18px;font-size:13px} .rev .er{color:#e0685f} .rev .kp{color:var(--accent2)} .rev .sh{color:var(--muted);font-variant-numeric:tabular-nums} + .tools{display:flex;gap:7px;flex-wrap:wrap;align-items:center;margin-top:10px} + .tools button{font-size:12px;padding:5px 10px} + .chk{font-size:12px;border-radius:999px;padding:1px 8px;border:1px solid var(--line);color:var(--muted)} + .chk.ok{color:var(--accent2);border-color:var(--accent2)} + .chk.bad{color:#e0685f;border-color:#e0685f} + .ins{border-top:1px dashed var(--line);text-align:center;padding:6px 0} + .ins button{font-size:12px;padding:4px 12px;color:var(--muted)} + .rough{width:100%;box-sizing:border-box;margin-top:8px;font:inherit;font-size:13px; + background:transparent;color:var(--ink);border:1px solid var(--accent); + border-radius:8px;padding:9px 11px;resize:vertical} + .busy{color:var(--accent);font-size:12px}
@@ -92,14 +101,12 @@ navBar("trailer"); var RUN = null, DRAFTS = [], CUR = null, PKG = {}, NOTES = {}, REVIEWS = []; - var timer = null; + var timer = null, EDITING = null; var runId = new URLSearchParams(location.search).get("run") || ""; var audio = new Audio(); if (!runId) { showMsg("msg", "No run was named in the link."); return; } - // A draft is busy while it is being written or drawn; a run is busy while it - // is reviewing or revising. Both are minutes long and report on the row. function busyDraft(d) { var s = String(d.draw_state || ""); return d.state === "writing" || (s && !/^(drawn|failed)/.test(s)); @@ -107,28 +114,42 @@ function busyRun() { return RUN && /^(writing|reviewing|revising)$/.test(String(RUN.stage || "")); } + // A shot is busy while it is being written, drawn or filmed. Any of those + // means the package on screen is about to change. + function busyShots() { + return (PKG.shots || []).some(function (s) { + return s.writing || s.drawing || s.filming; }); + } - async function load(repaint) { + async function load() { var r = await api("run_status", { run_id: runId }); if (!r.ok) { showMsg("msg", r.error || "Could not load that run."); return; } - var wasBusy = (DRAFTS.length && DRAFTS.some(busyDraft)) || busyRun(); RUN = r.run; DRAFTS = r.drafts || []; document.getElementById("title").textContent = RUN.book_title || "Trailers"; document.getElementById("sub").textContent = sub(); if (!CUR && DRAFTS.length) CUR = leadDraftId() || DRAFTS[0].id; paintTabs(); - var rv = await api("list_reviews", { run_id: runId }); REVIEWS = (rv && rv.reviews) || []; + await openTab(CUR); + tick(); + } - var busy = DRAFTS.some(busyDraft) || busyRun(); - if (busy && !timer) timer = setInterval(load, 5000); + function tick() { + var busy = DRAFTS.some(busyDraft) || busyRun() || busyShots(); + if (busy && !timer) timer = setInterval(refresh, 5000); if (!busy && timer) { clearInterval(timer); timer = null; } + } - var me = DRAFTS.filter(function (d) { return d.id === CUR; })[0]; - if (me && me.state === "writing") { paintWriting(); return; } - if (repaint !== false && (wasBusy || !document.querySelector(".shot"))) openTab(CUR); - else paintBar(); + // A refresh keeps the tab you are on and the box you are typing in. + async function refresh() { + var r = await api("run_status", { run_id: runId }); + if (!r.ok) return; + RUN = r.run; DRAFTS = r.drafts || []; + document.getElementById("sub").textContent = sub(); + paintTabs(); + await openTab(CUR); + tick(); } function leadDraftId() { @@ -146,6 +167,7 @@ bits.push(DRAFTS.length + " script" + (DRAFTS.length === 1 ? "" : "s")); var spent = DRAFTS.reduce(function (a, d) { return a + Number(d.write_usd || 0) + Number(d.draw_usd || 0); }, 0); + (PKG.shots || []).forEach(function (s) { spent += Number(s.film_usd || 0); }); if (spent) bits.push("$" + spent.toFixed(2) + " so far"); if (RUN.created_at) bits.push(when(RUN.created_at)); return bits.join(" · "); @@ -159,30 +181,21 @@ : (s && !/^(drawn|failed)/.test(s)) ? '' + esc(s) + '' : d.score ? '' + d.score + '' : ''; - var isLead = d.writer === RUN.lead_writer; return ''; + + (d.writer === RUN.lead_writer ? ' ★' : '') + extra + ''; }).join(""); } - function paintWriting() { - document.getElementById("pane").innerHTML = - 'Working. Reading the whole book takes a few minutes.
'; - } - document.getElementById("tabs").addEventListener("click", function (e) { var id = e.target.closest ? e.target.closest(".tab") : null; if (!id) return; - CUR = id.getAttribute("data-tab"); + CUR = id.getAttribute("data-tab"); EDITING = null; paintTabs(); openTab(CUR); }); - // ------------------------------------------------------------ one trailer - async function openTab(id) { var meta = DRAFTS.filter(function (d) { return d.id === id; })[0]; if (!meta) return; @@ -194,8 +207,10 @@ + esc(meta.id) + '">Try this writer again'; return; } - if (meta.state === "writing") { paintWriting(); return; } - pane.innerHTML = 'Loading…
'; + if (meta.state === "writing") { + pane.innerHTML = 'Working. Reading the whole book takes a few minutes.
'; + return; + } var r = await api("get_draft", { draft_id: id }); if (!r.ok) { pane.innerHTML = '' + esc(r.error || "") + '
'; return; } PKG = r.draft.package || {}; @@ -204,95 +219,77 @@ } function barHtml(meta) { - var shots = (PKG.shots || []).length, cast = (PKG.characters || []).length; - var imgs = cast * 3 + shots * 2; - var price = imgs * 0.068; + var shots = (PKG.shots || []), cast = (PKG.characters || []); + var undrawn = shots.filter(function (s) { return !s.opening_url; }).length; + var unfilmed = shots.filter(function (s) { return !s.clip_url; }).length; + var filmed = shots.length - unfilmed; + var imgs = cast.length * 3 + shots.length * 2; var s = String(meta.draw_state || ""); var drawing = s && !/^(drawn|failed)/.test(s); - var drawn = /^drawn/.test(s); var isLead = meta.writer === RUN.lead_writer; var out = []; - // 1. Whose concept wins. The author's call, not a score's. - if (!isLead) { - out.push(''); - } else { - out.push('★ head writer'); - } + if (!isLead) out.push(''); + else out.push('★ head writer'); - // 2. Drawing. - if (drawing) { - out.push(''); - } else { - out.push(''); - if (!drawn) out.push('' + imgs + ' pictures'); - else out.push('' + esc(s) - + (meta.draw_usd ? ' · $' + Number(meta.draw_usd).toFixed(2) : '') + ''); - } + if (drawing) out.push(''); + else if (undrawn) out.push(''); + else out.push('storyboard drawn' + + (meta.draw_usd ? ' · $' + Number(meta.draw_usd).toFixed(2) : '') + ''); - // 3. Review, revise, film - only meaningful on the head writer's tab. if (isLead) { var others = DRAFTS.filter(function (d) { return d.writer !== RUN.lead_writer && /^ready/.test(String(d.state)); }).length; - if (RUN.stage === "reviewing") { + if (RUN.stage === "reviewing") out.push(''); - } else if (others) { + else if (others) out.push(''); - } - if (RUN.stage === "revising") { + + (REVIEWS.length ? "Review it again" : "Have the others review this") + ''); + if (RUN.stage === "revising") out.push(''); - } else if (REVIEWS.length || Object.keys(NOTES).length) { - out.push(''); - } - if (drawn) { + else if (REVIEWS.length || Object.keys(NOTES).length) + out.push(''); + + if (unfilmed && !undrawn) out.push(''); - } else { - out.push('Draw the storyboard before filming — ' - + 'every shot is filmed from its opening frame.'); - } + + '">Film ' + (filmed ? 'the ' + unfilmed + ' remaining shots' : 'all ' + shots.length + ' shots') + + ' · $' + (unfilmed * 0.75).toFixed(2) + ''); + if (filmed) + out.push(''); } return out.join(""); } - function paintBar() { - var el = document.querySelector(".bar"); - var meta = DRAFTS.filter(function (d) { return d.id === CUR; })[0]; - if (el && meta) el.innerHTML = barHtml(meta); - } - function reviewsHtml(meta) { var mine = REVIEWS.filter(function (r) { return r.subject === meta.writer && Number(r.iteration) === Number(meta.iteration); }); if (!mine.length) return ""; return 'The writer is turning your note into a shot…
' + + '' + esc(String(s.rough || "").slice(0, 300)) + '
' + esc(s.action || "") + '
' + (s.camera ? 'camera: ' + esc(s.camera) + '
' : '') + (s.continuity ? 'continuity: ' + esc(s.continuity) + '
' : '') + lines + + '