From c02eb5d511f19e9d1f21082ce8f122f3af8bff1e Mon Sep 17 00:00:00 2001
From: gator
Date: Sat, 19 Sep 2026 15:16:26 +0000
Subject: [PATCH] Looks per scene, a change prompt on every character picture,
click to enlarge
Each scene now shows who is in it as they look IN THAT SCENE, with one box:
'Change how Stewart looks from this scene on'. Cast cards show every look
front, side and back. Every picture opens large. The storyboard waits until
all pictures exist. A box for the author's notes on the script as a whole,
and Revise is always on offer.
---
trailer.html | 177 +++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 165 insertions(+), 12 deletions(-)
diff --git a/trailer.html b/trailer.html
index a66df1d..1533051 100644
--- a/trailer.html
+++ b/trailer.html
@@ -28,7 +28,37 @@
.ch .ap{color:var(--muted);font-size:12px;margin-top:4px}
.sheets{display:flex;gap:5px;margin-bottom:8px}
.sheets img{width:33%;border-radius:6px;border:1px solid var(--line);display:block;
- aspect-ratio:16/9;object-fit:cover}
+ aspect-ratio:3/4;object-fit:cover;object-position:top}
+ img[data-zoom]{cursor:zoom-in}
+ .lookset{border-top:1px dashed var(--line);margin-top:8px;padding-top:8px}
+ .lookset .lw{color:var(--muted);font-size:12px;margin-bottom:5px}
+ .lookset .lw b{display:inline;color:inherit}
+ .lookset.idle{opacity:.55}
+ .whoin{display:flex;flex-wrap:wrap;gap:10px;margin:10px 0}
+ .whoin .p{display:flex;gap:8px;border:1px solid var(--line);border-radius:10px;
+ padding:8px;width:310px;font-size:12px;align-items:flex-start}
+ .whoin .p img{width:64px;aspect-ratio:3/4;object-fit:cover;object-position:top;
+ border-radius:6px;border:1px solid var(--line);flex:none}
+ .whoin .p .none{width:64px;aspect-ratio:3/4;border-radius:6px;flex:none;
+ border:1px dashed var(--line);display:flex;align-items:center;
+ justify-content:center;color:var(--muted);font-size:10px;text-align:center}
+ .whoin .p .t{flex:1;min-width:0}
+ .whoin .p .t b{display:block;font-size:13px}
+ .whoin .p .t .w{color:var(--muted);margin:2px 0 6px}
+ .whoin .p .t .st{color:#b4531f}
+ .whoin .p .t .new{color:#1f7a3a;font-weight:600}
+ .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}
+ .whole{border:1px solid var(--line);border-radius:10px;padding:12px;margin:0 0 18px}
+ .whole h4{margin:0 0 4px;font-size:14px}
+ .whole p{margin:0 0 8px;color:var(--muted);font-size:13px}
+ .whole textarea{width:100%;box-sizing:border-box}
+ #zoom{position:fixed;inset:0;background:rgba(0,0,0,.9);z-index:99;display:none;
+ align-items:center;justify-content:center;cursor:zoom-out;padding:18px}
+ #zoom.on{display:flex}
+ #zoom img{max-width:100%;max-height:100%;border-radius:6px}
+ #zoom .cap{position:fixed;left:0;right:0;bottom:10px;text-align:center;color:#ddd;font-size:13px}
.shot{border-top:1px solid var(--line);padding:16px 0;display:flex;gap:16px}
.shot .num{width:40px;flex:none;color:var(--muted);font-variant-numeric:tabular-nums}
.shot .body{flex:1;min-width:0}
@@ -191,6 +221,8 @@
}
function busyShots() {
+ if (String(PKG.look_state || "")
+ && (Date.now() - (Date.parse(PKG.look_started || "") || 0)) < 15 * 60 * 1000) return true;
return (PKG.shots || []).some(function (s) {
return s.writing || s.drawing || stillFilming(s); });
}
@@ -413,7 +445,10 @@
out.push('');
else {
var over = (PKG.timing && Number(PKG.timing.over_by)) || 0;
- if (REVIEWS.length || Object.keys(NOTES).length || over > 5)
+ // Always on offer. It used to appear only once a note existed, and
+ // a note typed a moment ago does not repaint the bar - so an author
+ // wrote their notes and found nothing to press.
+ if (true)
out.push('
';
if (!REEL) {
+ // A storyboard is its pictures. With some missing it plays as a slide
+ // show with holes, which tells the author nothing about the sequence.
+ var toDraw = Number(PKG.pictures_to_draw || 0);
+ if (toDraw > 0)
+ out += '
' + toDraw + ' picture'
+ + (toDraw === 1 ? ' is' : 's are') + ' still to be drawn. Use Draw this storyboard '
+ + 'above, then come back here.
';
var body = insertHtml("start");
@@ -772,7 +816,81 @@
return head + probs + '
' + barHtml(meta) + '
'
+ '
' + reelHtml() + '
'
- + spineHtml() + cutsHtml(meta) + reviewsHtml(meta) + castHtml + body;
+ + spineHtml() + cutsHtml(meta) + reviewsHtml(meta) + wholeHtml() + castHtml + body;
+ }
+
+ // Every look a character has, in the order the story reaches them: the
+ // clothes in words, then front, side and back. A look nobody wears any more
+ // is kept (the author may change their mind back) but shown faded.
+ function looksHtml(c) {
+ var looks = c.looks || {};
+ var keys = Object.keys(looks).sort(function (a, b) {
+ return String(looks[a].first_shot || "").localeCompare(String(looks[b].first_shot || "")); });
+ if (!keys.length) return "";
+ var nm = c.name || c.id;
+ return keys.map(function (k) {
+ var L = looks[k], pics = [["front_url", "front"], ["side_url", "side"], ["back_url", "back"]];
+ var imgs = pics.map(function (p) {
+ return L[p[0]] ? '' : ''; }).join("");
+ return '
';
+ }).join("");
+ }
+
+ // WHO IS IN THIS SCENE, as they look in it. This is where an author changes
+ // a hat, a dress, a cut lip - in words, about one person, in one scene - and
+ // it holds from here on until they change it again.
+ function whoInHtml(s) {
+ var ids = s.visible_characters || [];
+ if (!ids.length) return "";
+ var byId = {}; (PKG.characters || []).forEach(function (c) { byId[String(c.id)] = c; });
+ var busy = !!String(PKG.look_state || "");
+ var prevLooks = {};
+ (PKG.shots || []).slice().sort(function (a, b) { return String(a.id).localeCompare(String(b.id)); })
+ .some(function (x) {
+ if (String(x.id) === String(s.id)) return true;
+ Object.keys(x.looks || {}).forEach(function (cid) { prevLooks[cid] = x.looks[cid]; });
+ return false; });
+ return '
' + ids.map(function (cid) {
+ cid = String(cid);
+ var c = byId[cid] || {}, key = (s.looks || {})[cid], L = (c.looks || {})[key] || {};
+ var nm = c.name || cid;
+ var pic = L.front_url || c.front_url || c.head_url || "";
+ var isNew = prevLooks[cid] && prevLooks[cid] !== key;
+ var asked = (s.look_asks || {})[cid];
+ return '
';
+ }
+
+ // The author's note on the script as a whole. It is an ordinary note filed
+ // under ALL, so it saves itself like the others and the head writer is shown
+ // it before anything else.
+ function wholeHtml() {
+ return '
Your notes on the script as a whole
'
+ + '
What is missing, what should go, what order the story should be told in, '
+ + 'what you want people to feel. The head writer reads this first when you press '
+ + 'Revise the whole script. Notes on a single shot go in the box under that shot.
'
+ + '
';
}
function insertHtml(after) {
@@ -831,12 +949,15 @@
+ 'preload="metadata">the filmed shot'
+ (s.clip_seconds ? ' · ' + Number(s.clip_seconds).toFixed(1) + 's' : '')
+ ''
- + (s.opening_url ? 'filmed from this frame' : '')
+ + (s.opening_url ? 'filmed from this frame' : '')
+ '';
} else if (s.opening_url || s.closing_url) {
media = '