From 676e56ae7fdd9574ae2e7b5799a769e4240aaecf Mon Sep 17 00:00:00 2001 From: simien Date: Fri, 2 Oct 2026 22:15:34 -0400 Subject: [PATCH 1/3] fix: fold the flashing warning into Motion's own description The Animation section opened with four lines of text before a single control: a heading, a label, a one-line description and a two-line health warning carrying its own hanging asterisk. A notice that takes that much room above the thing it warns about gets read as chrome and skipped, which is the opposite of what a photosensitivity warning is for. The warning is now the second sentence of Motion's own description, where it sits one line above the select it applies to. Nothing is lost: the advice to skip Motion if flashing affects you is still there, in the same muted colour, on the line a visitor reads before choosing. The .hint-warn rules go with it, and so does .field .hint + .hint, which no longer matches anything in the markup or in the hints buildLayerControls writes. Co-Authored-By: Claude Opus 5 --- index.html | 14 +++++++++----- style.css | 31 ------------------------------- 2 files changed, 9 insertions(+), 36 deletions(-) diff --git a/index.html b/index.html index f5c1001..fbfa22a 100644 --- a/index.html +++ b/index.html @@ -193,7 +193,7 @@ files (not the vendored third-party ones, which are pinned and don't change). Bump it whenever style.css or generator.js changes, or a returning visitor's cached copy silently goes stale. --> - + + it moves, a still is the default, and the flash warning here + should not be the first line a new visitor reads. + + The warning is a sentence in the description rather than a + paragraph of its own: the section was four lines tall before + a single control, and a notice that takes that much room gets + read past as chrome. -->

Animation

-

Plays on the canvas as soon as you pick one.

-

Motion flashes the whole canvas. Skip it if flashing affects you.

+

Plays as soon as you pick one. Skip it if flashing affects you.

+ Opens at thirteen and a half, well up from the three it + used to and the two the example clips in examples/ were + rendered at. A background is looked past rather than at, + and at three seconds a whole turn of the field goes by + inside a glance, which reads as something demanding + attention rather than something behind the work. Slow + enough and the canvas is never seen to move, only to have + moved. The ladder is eight steps to a doubling, so step 46 + is 13454ms, which the panel rounds to 13.5s. + + This is long enough to matter to a GIF. One pass is 168 + frames at the 80ms the preview draws at, and the byte + budget below affords 69 on the default canvas, so an + export is slow motion at about 5fps. That reads fine at + this cycle length and is what GIF_FRAME_BUDGET's comment + anticipates; it is not a sign the budget is wrong. --> +
- + either. Stepped is the difference between them. + + Opens at four, not the two it used to. Two seconds is + about as long as it takes to notice what a composition is + doing, so rerolling on it means never seeing one settle; + four gives each one a beat to be looked at before the next + replaces it. Step 32 is exactly 4000ms. --> +

Saved states

@@ -896,7 +912,7 @@

Export & share

It stops at three. Five and eight were here, and under Motion they are what took an export to 1.9GB of retained - frames on the Web preset: the budget now trims that, but + frames at 1080p: the budget now trims that, but what it trims is the frames within each pass, so both settings bought more passes by making every one of them too coarse to read as motion. They cost nothing with @@ -2060,7 +2076,7 @@

Flield: seamless animated b // frame is width x height x 4 bytes and an export retains all of // them at once, before the encoder has allocated anything of its // own. Measured on the default 1200x630 canvas that is 2.9MB a - // frame, so the 240 above is 700MB; on the Web preset's 1920x1080 + // frame, so the 240 above is 700MB; on a hand-typed 1920x1080 // it is 7.9MB a frame and 1.9GB, which is past what a phone's tab // survives and enough to hang a desktop one. Canvas size is a free // text field, so there is no size this can assume. Whichever of the @@ -2390,9 +2406,15 @@

Flield: seamless animated b render(); } - // Web and Tablet are real, recognizable resolutions (1080p; iPad - // portrait) that also happen to divide evenly by most of the block - // size range. Mobile's old 375x812 (iPhone X) was both a narrower, + // Web is the 1200x630 the document already opens at, so the chip is + // lit on arrival rather than offering a size nothing starts on. It + // is a wide canvas to work from, not a screen to fill: the + // width field is free text, so anyone wanting 1080p or wider types + // it, and nobody wanting a card-sized background has to shrink one. + // Tablet is a real, recognizable resolution (iPad portrait). Both + // divide evenly by most of the block size range. + // + // Mobile's old 375x812 (iPhone X) was both a narrower, // dated reference and the one preset that couldn't tile evenly at // any block size: 375 is odd, so no even block size divides it, and // 812 only shares factors with a handful of block sizes (2, 4, 7, @@ -2409,7 +2431,7 @@

Flield: seamless animated b // so Tile forces it off. 512 is a nice round, highly divisible size // for a texture swatch, not a specific device or screen to match. const DIMENSION_PRESETS = { - web: { width: 1920, height: 1080 }, + web: { width: 1200, height: 630 }, tablet: { width: 768, height: 1024 }, mobile: { width: 400, height: 800 }, tile: { width: 512, height: 512, shapeMask: "none", symmetry: "quad" }, @@ -2444,7 +2466,7 @@

Flield: seamless animated b // Lights a preset chip while the current values still match it, // derived from state rather than from the click: Web is lit however - // the size came to be 1920x1080, Tile only while the size is square + // the size came to be 1200x630, Tile only while the size is square // with no mask and both layers on 4-way mirror, and a palette chip // only while all six of its ranges still hold. Nudge anything and // the chip goes quiet on its own. From 49fea61b6fb23a4863dab1bc8f484f1937d07f7a Mon Sep 17 00:00:00 2001 From: simien Date: Fri, 2 Oct 2026 22:22:48 -0400 Subject: [PATCH 3/3] fix: fetch a pull request's base at full depth so file history survives The checks workflow fetched the base branch with --depth=1. That writes .git/shallow and grafts the branch one commit deep, and because the base shares its history with the branch under test, the graft truncates the whole clone. git log -1 -- then reports the graft point instead of the commit that last touched the file. Nothing noticed until now because the shallow fetch only runs when github.base_ref is set, which is to say only on a pull request, and every change since the sitemap-dates check was added has gone straight to main. The first pull request after it failed on four pages at once, each one named as changed today when no commit on the branch touches them. actions/checkout already runs at fetch-depth: 0, so the full history is paid for before this step. Dropping --depth=1 costs nothing and gives the checks the history they read. Reproduced on a clone of this repo: with --depth=1, guide/index.html reports today; without it, 2026-09-29, which is what sitemap.xml says. Co-Authored-By: Claude Opus 5 --- .github/workflows/checks.yml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 06d75a2..ba657d3 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -31,10 +31,21 @@ jobs: # commit the push carried at once. That SHA is all zeros on a # branch's first push and may be gone after a force push, so it # is only used when the clone actually has it. + # + # The base is fetched at full depth, not --depth=1. A shallow + # fetch writes .git/shallow and grafts the branch one commit + # deep, and because the base shares its history with this one + # that truncates the whole clone: git log -- then reports + # the graft point rather than the commit that last touched the + # file, which is what the sitemap-dates check reads. It failed + # on every page at once the first time a pull request ran after + # that check was added, naming today's date for files nobody had + # touched. fetch-depth: 0 above already pays for the history, so + # there is nothing to save here. run: | BASE="" if [ -n "${{ github.base_ref }}" ]; then - git fetch --no-tags --depth=1 origin "${{ github.base_ref }}" + git fetch --no-tags origin "${{ github.base_ref }}" BASE="origin/${{ github.base_ref }}" elif git cat-file -e "${{ github.event.before }}^{commit}" 2>/dev/null; then BASE="${{ github.event.before }}"