diff --git a/trailer.html b/trailer.html index c49cfea..728dcaa 100644 --- a/trailer.html +++ b/trailer.html @@ -60,14 +60,20 @@ border-radius:999px;padding:0 6px} .vobadge{color:var(--muted);font-size:11px;border:1px solid var(--line); border-radius:999px;padding:0 6px} - .revs{margin:0 0 22px} - .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 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} + .gaps{border:1px solid var(--accent);border-radius:10px;padding:14px 16px;margin:0 0 14px} + .gaps h4{margin:0 0 4px;font-size:15px;color:var(--accent)} + .gaps .verdict{font-size:14px;margin:0 0 10px} + .gaps ol{margin:0;padding-left:20px;font-size:13px} + .gaps li{margin-bottom:7px} + .gaps .fix{color:var(--muted);display:block} + .gaps .sh{color:var(--muted);font-variant-numeric:tabular-nums} + .rd{border:1px solid var(--line);border-radius:10px;padding:12px 14px;margin-bottom:10px} + .rd h4{margin:0 0 6px;font-size:14px} + .rd h4 .n{color:var(--accent2);font-weight:600;margin-left:8px} + .rd blockquote{margin:0 0 8px;font-size:14px;border-left:2px solid var(--line);padding-left:10px} + .rd .kv{color:var(--muted);font-size:12.5px;margin:2px 0} + .rd ul{margin:6px 0 0;padding-left:18px;font-size:13px} + .rd .wrong{color:#e0685f} .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)} @@ -114,8 +120,6 @@ 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; }); @@ -141,13 +145,14 @@ if (!busy && timer) { clearInterval(timer); timer = null; } } - // 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(); + var rv = await api("list_reviews", { run_id: runId }); + REVIEWS = (rv && rv.reviews) || []; await openTab(CUR); tick(); } @@ -179,7 +184,8 @@ var extra = d.state === "writing" ? 'writing…' : d.state === "failed" ? 'failed' : (s && !/^(drawn|failed)/.test(s)) ? '' + esc(s) + '' - : d.score ? '' + d.score + '' + : d.score ? '' + + d.score + '' : ''; return ''); else if (undrawn) out.push(''); else out.push('storyboard drawn' + (meta.draw_usd ? ' · $' + Number(meta.draw_usd).toFixed(2) : '') + ''); @@ -244,10 +251,12 @@ var others = DRAFTS.filter(function (d) { return d.writer !== RUN.lead_writer && /^ready/.test(String(d.state)); }).length; if (RUN.stage === "reviewing") - out.push(''); + out.push(''); else if (others) - out.push(''); + out.push(''); if (RUN.stage === "revising") out.push(''); else if (REVIEWS.length || Object.keys(NOTES).length) @@ -264,26 +273,55 @@ return out.join(""); } + // What readers understood, and what the writer says it failed to say. 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 '
' + mine.map(function (r) { - function list(items, cls, fmt) { - return (!items || !items.length) ? "" - : ''; - } - return '

' + esc(r.label) + '' + esc(r.score) + '/100

' - + list(r.errors, "er", function (e) { - return '
  • ' + (e.shot ? 'shot ' + esc(e.shot) + ' ' : '') - + esc(e.problem || "") + (e.fix ? ' — ' + esc(e.fix) + '' : '') + '
  • '; }) - + list(r.suggestions, "", function (s) { - return '
  • ' + (s.shot ? 'shot ' + esc(s.shot) + ' ' : '') - + esc(s.change || "") + '
  • '; }) - + list(r.strengths, "kp", function (s) { - return '
  • ' + esc(typeof s === "string" ? s : JSON.stringify(s)) + '
  • '; }) + var gaps = mine.filter(function (r) { return r.reviewer === "__gaps__"; })[0]; + var readers = mine.filter(function (r) { return r.reviewer !== "__gaps__"; }); + var out = ""; + + if (gaps) { + var v = gaps.strengths && gaps.strengths.verdict; + var list = gaps.suggestions || []; + out += '

    What this trailer does not tell a viewer

    ' + + (v ? '

    ' + esc(v) + '

    ' : '') + + (list.length ? '
      ' + list.map(function (g) { + return '
    1. ' + esc(g.what || "") + + 'shot ' + esc(g.shot || "?") + ' → ' + + esc(g.fix || "") + '
    2. '; }).join("") + '
    ' : '') + '
    '; - }).join("") + '
    '; + } + + out += readers.map(function (r) { + var s = r.strengths || {}, e = r.errors || {}; + if (typeof s !== "object") s = {}; + if (typeof e !== "object") e = {}; + function ul(items, cls) { + return (!items || !items.length) ? "" + : ''; + } + return '

    ' + esc(r.label) + ' watched it without reading the book' + + (r.score ? '' + + r.score + '/100' : '') + '

    ' + + (s.summary ? '
    ' + esc(s.summary) + '
    ' : '') + + '
    main character: ' + esc(s.who || "not clear to them") + '
    ' + + '
    what they want: ' + esc(s.wants || "not clear to them") + '
    ' + + '
    what is in the way: ' + esc(s.obstacle || "not clear to them") + '
    ' + + (s.would_watch ? '
    would they watch the film: ' + + esc(s.would_watch) + '
    ' : '') + + (s.unanswered && s.unanswered.length + ? '
    still asking:
    ' + ul(s.unanswered, "") : '') + + (e.missed && e.missed.length ? '
    they missed:
    ' + ul(e.missed, "") : '') + + (e.got_wrong && e.got_wrong.length + ? '
    the trailer misled them:
    ' + ul(e.got_wrong, "wrong") : '') + + '
    '; + }).join(""); + + return '
    ' + out + '
    '; } function paneHtml(meta, draft) { @@ -318,9 +356,7 @@ }).join("") + '
    '; var body = insertHtml("start"); - shots.forEach(function (s) { - body += shotHtml(s, voice) + insertHtml(s.id); - }); + shots.forEach(function (s) { body += shotHtml(s, voice) + insertHtml(s.id); }); return head + probs + '
    ' + barHtml(meta) + '
    ' + reviewsHtml(meta) + castHtml + body; @@ -420,8 +456,6 @@ + ''; } - // ------------------------------------------------------------ actions - async function act(name, payload, msg) { var r = await api(name, payload); if (!r.ok) { showMsg("msg", r.error || "That did not work."); return null; } @@ -431,7 +465,7 @@ } document.getElementById("pane").addEventListener("click", async function (e) { - var hit = function (attr) { var el = e.target.closest("[" + attr + "]"); return el; }; + var hit = function (attr) { return e.target.closest("[" + attr + "]"); }; var el, r; if ((el = hit("data-cancel"))) { EDITING = null; openTab(CUR); return; } @@ -443,14 +477,14 @@ if (rough.length < 20) { showMsg("msg", "Say what happens in the scene."); return; } el.disabled = true; r = await act("add_shot", { draft_id: CUR, after: el.getAttribute("data-addgo"), rough: rough }); - EDITING = null; if (r) { await refresh(); } return; + EDITING = null; if (r) await refresh(); return; } if ((el = hit("data-rewrite"))) { var rw = document.getElementById("rough").value.trim(); if (rw.length < 20) { showMsg("msg", "Say what should change."); return; } el.disabled = true; r = await act("write_shot", { draft_id: CUR, shot_id: el.getAttribute("data-rewrite"), rough: rw }); - EDITING = null; if (r) { await refresh(); } return; + EDITING = null; if (r) await refresh(); return; } if ((el = hit("data-drawone"))) { el.disabled = true;