From d21360ea2be99ef8c6923b5fb01c9e317d20c075 Mon Sep 17 00:00:00 2001
From: gator
Date: Thu, 17 Sep 2026 06:31:29 +0000
Subject: [PATCH] trailer.html: edit, draw, film and reassemble one shot at a
time
The trailer was all-or-nothing. If one shot was wrong - a line cut short, a
character duplicated, a scene missing from the front - the only lever was to
film all twenty-four again.
Every shot now carries its own controls: describe a change and have the writer
turn it into a proper shot, draw its two frames, film just that one, watch the
clip that came back, and read the machine check that compared what it says with
what the script says. "Add a scene here" sits between shots, because the note
that prompted this was that the trailer needed an opening the writer never
thought to include.
Assemble is separate and cheap - it joins whatever has been filmed - so
re-cutting after changing one shot costs nothing but the one shot.
Prices are on the buttons because they differ by two orders of magnitude:
rewriting a shot is cents, drawing it is fourteen, filming it is about a
dollar.
---
trailer.html | 424 ++++++++++++++++++++++++++++++---------------------
1 file changed, 250 insertions(+), 174 deletions(-)
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 = '