diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bd63dd71..00337ba1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,6 +8,11 @@ on: jobs: verify: runs-on: ubuntu-latest + # A healthy run is about 4 minutes. Without a limit GitHub allows 360, and on + # 2026-10-23 three runs hung in the Chromium install for 7-10 minutes each, + # holding a pull request until somebody noticed and cancelled. The install has + # its own retries below; this is only the floor under everything else. + timeout-minutes: 20 steps: - uses: actions/checkout@v5 @@ -77,8 +82,41 @@ jobs: # Integration coverage for the parts that only exist in a browser: # SharedWorker startup, cross-tab BroadcastChannel fan-out, IndexedDB. + # + # The browser is cached, keyed on Playwright's version, so most runs do not + # download it at all. The download is what hung: three times on 2026-10-23, + # 7-10 minutes each against 20-27 seconds healthy, and a re-run on another + # runner always went straight through. So each attempt gets two minutes and + # three tries, and a stall costs two minutes rather than a cancelled run. + # The system packages (`install-deps`) still install every run -- they are + # not in the cache path -- under the same retries. + - name: Find Playwright's version + id: playwright + run: echo "version=$(apps/fixture/node_modules/.bin/playwright --version | awk '{print $2}')" >> "$GITHUB_OUTPUT" + + - name: Cache Chromium + id: browsers + uses: actions/cache@v5 + with: + path: ~/.cache/ms-playwright + key: playwright-chromium-${{ runner.os }}-${{ steps.playwright.outputs.version }} + - name: Install Chromium - run: pnpm --filter @single-studio/fixture exec playwright install --with-deps chromium + timeout-minutes: 8 + env: + CACHED: ${{ steps.browsers.outputs.cache-hit }} + run: | + if [ "$CACHED" = true ]; then what='install-deps chromium'; else what='install --with-deps chromium'; fi + + for attempt in 1 2 3; do + # `timeout` signals the whole process group, so a hung apt-get under + # sudo goes with Playwright rather than holding the dpkg lock. + if timeout --kill-after=10 120 apps/fixture/node_modules/.bin/playwright $what; then exit 0; fi + echo "::warning::Chromium install attempt $attempt stalled or failed after up to 2 minutes; trying again" + done + + echo "::error::Chromium did not install in three attempts" + exit 1 - name: Smoke test working-directory: apps/fixture diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 0474e95b..d3c4f95c 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -37,6 +37,9 @@ concurrency: jobs: build: runs-on: ubuntu-latest + # 19-28 seconds when healthy. A limit so a hung step fails rather than holding + # the pages concurrency group, and with it the next deploy, for GitHub's 360. + timeout-minutes: 10 steps: - uses: actions/checkout@v5 @@ -76,6 +79,8 @@ jobs: deploy: needs: build runs-on: ubuntu-latest + # 9-13 seconds when healthy. + timeout-minutes: 10 environment: name: github-pages url: ${{ steps.deploy.outputs.page_url }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 110fd8ca..8a6ecdbe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,44 @@ Every published package shares a version — `@single-studio/core`, `@single-studio/provider-supabase` and the four plugins go out as one release. The tag is checked against all six manifests, so they cannot drift apart. +## 0.14.0 + +Three findings from a studio running 0.13.0 on client work, each measured on a live +board. A minor because a `col-auto` image picker now stops at a width, and a board's +own spacing moved from Tailwind utilities in the markup to `base.css`: a studio that +overrode those utilities by name should move to the properties below. + +### Added + +- **The board's spacing as properties.** `--ss-panel-pad`, `--ss-board-pad`, + `--ss-board-gap`, `--ss-bar-pad-x` and `--ss-bar-pad-y`, set on `.ss-control`. They + default to what the board always had, stepping up at 640px; a studio that sets one + gets it at every width -- in an OBS dock the viewport is the dock, so the step + re-flowed the board as an operator dragged its edge. The board's `
` has a + class of its own, `.ss-board`, instead of being reachable only as + `.ss-control > main`. +- **`--ss-picker-max`**, the widest a `col-auto` `ImagePicker` grows. Defaults to + `9.25rem`. + +### Changed + +- **A `col-auto` `ImagePicker` stops at `--ss-picker-max`.** A picker is a `` and so as wide as the longest key in the library -- something +the operator sets at run time. Measured on a 640px board by renaming one asset: + +| Longest key | Picker | The Field beside it | +| ------------------------------------- | ------ | ------------------- | +| `x` | 136px | 255px | +| `broadcaster` | 202px | 188px | +| `northwind-esports-primary-logo-2026` | 365px | 25px | + +A board checked at design time degrades months later because somebody named a logo +descriptively, and the neighbour that loses is usually the name field. + +The ask is a cap inside the utility, where a breakpoint variant wraps it too: +`.ss-row > &.ss-image-picker { max-width: var(--ss-picker-max, 9.25rem) }`. A studio +cannot write that rule itself: `sm:col-auto` is a literal class at every width, so an +attribute selector also matches below the breakpoint, where the picker has a line to +itself and must fill it. The studio carries a width-band cap meanwhile, which also +holds back pickers that are not `col-auto` -- about 338px unused at 700px in one panel. + +Open before building: whether the cap belongs to `ImagePicker` alone or to any control +sized by operator content, and whether a cap or a select that does not size to its +options is the better fix. + +### Reading an `--ss-*` property, and a dev server that serves half a stylesheet + +Not core's code, but core's users hit it, because core's layer runs on custom +properties. `max-width: var(--undefined)` is invalid at computed-value time and +resolves to `none` -- the same as a stylesheet that never loaded -- so a misspelt or +renamed property fails in silence. The studio met it twice in one day through Vite's +dev server: once a hot update applied one of two edits to the same file, and once an +auto-restart (a branch checkout touched `vite.config.js`'s mtime) brought the CSS +back with Tailwind's theme tokens but not one utility class. `npm run build` was right +both times; a restart fixed both. + +The ask is docs only, where `getting-started.md` introduces the properties: + +- read an `--ss-*` property with a fallback, `var(--ss-basis, 0%)`, so a typo or a + rename degrades to a default rather than to nothing; +- when a rule has no effect, read the page's served `