Archilyzer · Source

archilyzer

Archilyzer
git clone https://archilyzer.pages.dev/source/archilyzer.git
Log | Files | Refs | README | LICENSE

commit d401d7d896448d0c33f9522fd87db5e0622f8719
parent 90b052e81724afba70c98cffcbcc30f7e2557829
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Fri,  2 Oct 2026 09:47:31 -0400

report-to-video: the posts feed's header is one row -- the platform and who posted -- with no running count and no caption, and the empty plate says only "No posts yet"; the 30 px the header gave back go to the stack

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Diffstat:
Mumtool/report-to-video/README.md | 7++++---
Mumtool/report-to-video/chrome-feed.mjs | 27+++++++--------------------
Mumtool/report-to-video/chrome-feed.test.mjs | 8+++++---
3 files changed, 16 insertions(+), 26 deletions(-)

diff --git a/umtool/report-to-video/README.md b/umtool/report-to-video/README.md @@ -1300,9 +1300,10 @@ moves: no hold, no footage move, and the cut is as long as its segments. posts to draw, so a popup schedule and one without posts are byte-identical to what they were. `postWindows` is empty for a feed. - **The column** (`chrome-feed.mjs`, one composition for the whole cut, - region-local at the column, transparent outside its panel): a header (the - platform and `@handle`, or "Posts" over several authors, a count, "posts as - the timeline reaches them"), then an empty state until the first post. Each + region-local at the column, transparent outside its panel): a one-row header + (the platform and `@handle`, or "Posts" over several authors -- no running + count and no caption, which a viewer reads off the column itself), then an + empty plate ("No posts yet") until the first post. Each post TICKS IN at the top, newest first: the cards already in slide down by its height over `push` while it enters from the column's outer edge, its accent rim flares and settles to a lit rail, which goes out when the next diff --git a/umtool/report-to-video/chrome-feed.mjs b/umtool/report-to-video/chrome-feed.mjs @@ -10,7 +10,7 @@ // What the column does // --------------------------------------------------------------------------- // Before the first post it shows its header (who posted, on what) and an -// empty state. Each post TICKS IN at its `in` (deck.mjs postSchedule: its +// empty plate. Each post TICKS IN at its `in` (deck.mjs postSchedule: its // clip's start + D, the transition's length -- the first clip's too, which // has no dissolve into it; several on one clip a `posts.seconds` apart): it lands at the TOP, newest first, as a timeline // reads, and every card already in slides DOWN by its height. The newest @@ -63,8 +63,9 @@ export const FEED_MOTION = Object.freeze({ }); /** - * The column's inner layout, region-local px: the padding, the header's rows - * and the stack area under it (`stack`: where cards are, and its height -- + * The column's inner layout, region-local px: the padding, the header (one + * row: who posted, on what -- nothing a viewer can't see for themselves) and + * the stack area under it (`stack`: where cards are, and its height -- * what `feedCues` fits them to). The card's own sizes: its text, meta and QR * cell. */ @@ -74,7 +75,7 @@ export function feedLayout(render) { const W = g.column.width, H = g.column.height; const padX = 22; const headerTop = 26; - const headerH = 66; + const headerH = 36; const ruleY = headerTop + headerH + 10; const stackY = ruleY + 18; const bottom = 22; @@ -105,8 +106,7 @@ export function feedLayout(render) { * * Card j of n sits at y = Σ (height + gap) of the cards newer than it that * are in; it is `c<j>` (autoAlpha, x, y), its highlight `h<j>` (opacity), the - * count `n<k>` (k posts in; autoAlpha), the empty state `empty`, the whole - * column `col` (x). + * empty plate `empty`, the whole column `col` (x). * * @returns {{ init: Record<string, object>, gone: Array<number|null>, * cues: Array<{ k: string, at: number, dur: number, from: object, to: object, ease: string, why: string }> }} @@ -124,7 +124,6 @@ export function feedCues({ init[`c${j}`] = { autoAlpha: 0, x: enterX, y: 0 }; init[`h${j}`] = { opacity: 0 }; } - for (let k = 0; k <= n; k += 1) init[`n${k}`] = { autoAlpha: k === 0 ? 1 : 0 }; const ev = []; const add = (k, at, dur, to, ease, why) => ev.push({ k, at: R(at), dur: R(Math.max(MIN, dur)), to, ease, why }); @@ -152,8 +151,6 @@ export function feedCues({ // The newest hands its highlight on. if (m > 0 && gone[m - 1] === null) add(`h${m - 1}`, t, calm, { opacity: 0 }, "power1.out", `calm for ${id}`); if (m === 0) add("empty", t, emptyOut, { autoAlpha: 0 }, "power1.out", `first ${id}`); - add(`n${m}`, t + lag, MIN, { autoAlpha: 0 }, "none", `count ${id}`); - add(`n${m + 1}`, t + lag, MIN, { autoAlpha: 1 }, "none", `count ${id}`); add(`c${m}`, t + lag, enter, { autoAlpha: 1, x: 0 }, "expo.out", `enter ${id}`); add(`h${m}`, t + lag + glowAt, glowUp, { opacity: 1 }, "power2.out", `glow ${id}`); add(`h${m}`, t + lag + glowAt + glowUp, glowDown, { opacity: newest }, "power2.inOut", `settle ${id}`); @@ -264,8 +261,6 @@ export function feedHtml(schedule, render, opts = {}) { ); }) .join("\n "); - const countHtml = Array.from({ length: posts.length + 1 }, (_, k) => - `<span class="n" data-k="n${k}">${k} of ${posts.length}</span>`).join(""); const vis = deckChoreography(schedule, render).visibility; const data = { @@ -321,11 +316,6 @@ export function feedHtml(schedule, render, opts = {}) { background: ${rgba(pal.accent, 0.26)}; box-shadow: inset 0 0 0 1px ${rgba(pal.accent, 0.7)}; } .title { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; font-family: 'DeckSansBold', sans-serif; font-size: 26px; line-height: 36px; letter-spacing: -0.005em; } - .count { flex: none; position: relative; width: 84px; height: 36px; font-size: 16px; line-height: 36px; - color: ${pal.muted}; font-variant-numeric: tabular-nums; } - .count .n { position: absolute; right: 0; top: 0; visibility: hidden; opacity: 0; } - .caption { margin-top: 8px; font-size: 14px; line-height: 20px; letter-spacing: 0.12em; text-transform: uppercase; - color: ${rgba(pal.muted, 0.9)}; white-space: nowrap; } .rule { position: absolute; left: ${lay.padX}px; top: ${lay.header.ruleY}px; width: ${W - 2 * lay.padX}px; height: 1px; background: ${rgba(pal.fg, 0.12)}; } .stack { position: absolute; left: ${lay.stack.x}px; top: ${lay.stack.y}px; width: ${lay.stack.width}px; @@ -335,7 +325,6 @@ export function feedHtml(schedule, render, opts = {}) { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; visibility: hidden; opacity: 0; } .empty b { font-family: 'DeckSansBold', sans-serif; font-weight: 400; font-size: 20px; color: ${rgba(pal.fg, 0.7)}; } - .empty span { font-size: 16px; color: ${pal.muted}; } /* A card: lifted off the column with a touch of the accent, its rail dim until it is the newest. Hidden until the timeline places it. */ .post { position: absolute; left: 0; top: 0; width: ${lay.stack.width}px; min-height: ${c.plate}px; @@ -385,13 +374,11 @@ export function feedHtml(schedule, render, opts = {}) { <div class="who-row"> ${who.platforms.map((p) => `<span class="platform">${esc(p)}</span>`).join("")} <span class="title">${esc(who.title)}</span> - <span class="count">${countHtml}</span> </div> - <div class="caption">Posts as the timeline reaches them</div> </div> <div class="rule"></div> <div class="stack"> - <div class="empty" data-k="empty"><b>No posts yet</b><span>They appear here as the cut reaches them</span></div> + <div class="empty" data-k="empty"><b>No posts yet</b></div> ${cardHtml} </div> </div> diff --git a/umtool/report-to-video/chrome-feed.test.mjs b/umtool/report-to-video/chrome-feed.test.mjs @@ -152,9 +152,10 @@ test("feedCues: each card ticks in at its time, at the top, and the ones in move // The highlight: the newest flares and settles; the one before it goes out. assert.deepEqual(byWhy(plan.cues, /^calm for b$/).map((c) => [c.k, c.to.opacity]), [["h0", 0]]); assert.deepEqual(byWhy(plan.cues, /^settle z$/).map((c) => [c.k, c.to.opacity]), [["h3", FEED_MOTION.newest]]); - // The empty state leaves with the first post; the count follows each one. + // The empty plate leaves with the first post; there is no count to keep. assert.deepEqual(byWhy(plan.cues, /^first a$/).map((c) => [c.k, c.at, c.to.autoAlpha]), [["empty", 10, 0]]); - assert.deepEqual(byWhy(plan.cues, /^count z$/).map((c) => [c.k, c.to.autoAlpha]), [["n3", 0], ["n4", 1]]); + assert.equal(plan.cues.some((c) => /^n\d+$/.test(c.k)), false); + assert.equal(Object.keys(plan.init).some((k) => /^n\d+$/.test(k)), false); assert.equal(plan.gone.every((x) => x === null), true); }); @@ -228,7 +229,8 @@ test("the page: one card per post with its date, words and QR; the header; the c assert.equal((html.match(/class="handle"/g) ?? []).length, 0); assert.ok(html.includes('<span class="date">Oct 19, 2024</span>')); assert.ok(html.includes('data-k="empty"')); - assert.ok(html.includes('<span class="n" data-k="n4">4 of 4</span>')); + // No running count and no caption: the header is who posted, on what. + assert.equal(/ of 4<|data-k="n\d|class="caption"|class="count"/.test(html), false); // The cue times are the schedule's. assert.deepEqual(dataOf(html).posts, s.posts.map((p) => ({ id: p.id, in: p.in }))); assert.equal(dataOf(html).column, feedLayout(FEED).stack.height);