From 87779edc82b2c9be24ab8245c7aae36614fbd3b4 Mon Sep 17 00:00:00 2001 From: Max <112043822+Maxaubert@users.noreply.github.com> Date: Sat, 19 Sep 2026 17:40:51 +0200 Subject: [PATCH] fix(settings): Tailwind scans core/, so the settings page has its layout back (#20) core/ sits outside the renderer root Tailwind scans, so utilities used only by the shared settings sections were never generated. The options e2e now measures the layout instead of only checking that rows exist. Co-Authored-By: Claude Fable 5.1 --- .gitignore | 1 + CLAUDE.md | 6 ++++++ core/README.md | 6 +++++- package-lock.json | 4 ++-- package.json | 2 +- src/renderer/src/index.css | 7 +++++++ tools/e2e/run.mjs | 34 ++++++++++++++++++++++++++++++++++ 7 files changed, 56 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 1664e9b..da01645 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ out dist *.log .e2e-profile +.e2e-shots/ diff --git a/CLAUDE.md b/CLAUDE.md index 1ceb632..2a3fb85 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -27,6 +27,12 @@ so an update never silently changes what an existing user sees; the bridge to ma userData), never shared. `settings/options.ts` lists every terminal option by id; a unit test holds the list and the sections together, and each app's e2e (`options`) asserts its page shows that list and no terminal-looking row of its own. A row outside the list is a fork. +- **A PAGE THAT WORKS IS NOT A PAGE THAT LOOKS RIGHT** (#20, 2026-09-19). Moving the settings into + `core/` dropped every Tailwind class used only there (`core/` is outside the scanned root; the fix + is the `@source` line at the top of `index.css`, do not remove it). All 14 e2e scenarios passed over + a ruined page, because they asserted that rows EXIST. The `options` scenario now MEASURES the + layout (card width, wall rows, row padding) and writes `.e2e-shots/settings-*.png`. After any change + that moves UI between `src/` and `core/`, LOOK at those screenshots before calling it done. - **A terminal change goes in `core/`**, and is a change to Prism too: say so in the PR, and ask the owner when it would conflict with how Prism works. App-shell changes (tabs, start screen, window) stay in `src/`. diff --git a/core/README.md b/core/README.md index ed431a5..38dffd9 100644 --- a/core/README.md +++ b/core/README.md @@ -62,7 +62,11 @@ ask me."* And: *"why can't this repo be the core?"* It can, and this is it. ## How each app consumes it -**Prism Terminal** (this repo): directly, through the app-side alias `@core`. +**Prism Terminal** (this repo): directly, through the app-side alias `@core`, +plus ONE line in `src/renderer/src/index.css`: `@source '../../../core';`. Tailwind +scans from the renderer root down and `core/` is outside it, so without the line +a utility used only in here is never generated, silently (#20: it took the whole +Settings page apart, with every functional test green). The app's tests, lint and the 14 e2e scenarios are the core's gate. **Prism**: as a dev dependency pinned to a tag of the `core-dist` branch, which diff --git a/package-lock.json b/package-lock.json index 88b0cb7..8c963d2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "prism-terminal", - "version": "0.1.0", + "version": "0.1.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "prism-terminal", - "version": "0.1.0", + "version": "0.1.1", "license": "MIT", "dependencies": { "@xterm/addon-fit": "^0.11.0", diff --git a/package.json b/package.json index f79def2..98d2b9c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "prism-terminal", "productName": "Prism Terminal", - "version": "0.1.0", + "version": "0.1.1", "description": "A tabbed Windows terminal for AI CLIs.", "main": "./out/main/index.js", "author": "Max", diff --git a/src/renderer/src/index.css b/src/renderer/src/index.css index 59ca0ac..9dae17d 100644 --- a/src/renderer/src/index.css +++ b/src/renderer/src/index.css @@ -1,4 +1,11 @@ @import 'tailwindcss'; +/* THE CORE IS OUTSIDE THE FOLDER TAILWIND SCANS (#20). Tailwind looks for class + names from the renderer root down, and the terminal lives in core/ at the + repo root, so a utility used ONLY there is never generated - silently. It + went unnoticed while every class in core/ also appeared in src/, and took the + whole Settings page apart the day the settings moved into the core. Prism + carries the same line for the same reason (see core/README.md). */ +@source '../../../core'; :root { color-scheme: dark; diff --git a/tools/e2e/run.mjs b/tools/e2e/run.mjs index 1e6d072..1b3b309 100644 --- a/tools/e2e/run.mjs +++ b/tools/e2e/run.mjs @@ -577,6 +577,40 @@ const scenarios = { const extra = [...shown].filter((id) => !wanted.includes(id) && !own.includes(id)) ok(extra.length === 0, `and nothing else claims to be a setting (extra: ${JSON.stringify(extra)})`) ok((await page.locator('[data-pref="confirm-close"]').count()) === 0, 'the close question is not a setting any more') + // AND IT IS LAID OUT (#20). The rows above all existed and all worked while + // the page was a ruin: core/ sits outside the folder Tailwind scans, so + // every utility used only by the shared sections was never generated, and + // a check that a row EXISTS cannot see that. Measured, so it can: a theme + // card has a card's width and the wall wraps into rows, a row has its + // padding, and the options of a segmented control do not overlap. + const look = await page.evaluate(() => { + const box = (e) => e.getBoundingClientRect() + const cards = [...document.querySelectorAll('[data-term-card]')].map(box) + const row = document.querySelector('[data-pref="term-font"]') + return { + cardWidth: Math.round(cards[0]?.width ?? 0), + cardRows: new Set(cards.map((c) => Math.round(c.top))).size, + rowPad: row ? parseFloat(getComputedStyle(row).paddingTop) : 0 + } + }) + await page.screenshot({ path: resolve(process.cwd(), '.e2e-shots/settings-appearance.png') }).catch(() => {}) + ok(look.cardWidth >= 150, `a theme card is a card, not a sliver (${look.cardWidth}px wide)`) + ok(look.cardRows >= 2, `and the wall wraps into rows (${look.cardRows})`) + ok(look.rowPad >= 8, `a settings row has its padding (${look.rowPad}px)`) + await page.locator('[data-settings-tab="general"]').click() + await sleep(300) + const overlaps = await page.evaluate(() => { + let n = 0 + for (const row of document.querySelectorAll('[data-pref]')) { + const b = [...row.querySelectorAll('button')].map((e) => e.getBoundingClientRect()).filter((r) => r.width > 0) + for (let i = 0; i < b.length; i += 1) + for (let j = i + 1; j < b.length; j += 1) + if (b[i].left < b[j].right - 1 && b[j].left < b[i].right - 1 && b[i].top < b[j].bottom - 1 && b[j].top < b[i].bottom - 1) n += 1 + } + return n + }) + ok(overlaps === 0, `no two controls in a row overlap (${overlaps} do)`) + await page.screenshot({ path: resolve(process.cwd(), '.e2e-shots/settings-general.png') }).catch(() => {}) await app.close().catch(() => {}) },