Prune repo to spec and stack-folder only.
Remove cards stack, rack, trace, slab, relay, vault, unused preview PNGs, and stack-only test scripts. Update home page, README, and Vite inputs. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -13,7 +13,6 @@ mkdirSync(out, { recursive: true });
|
||||
|
||||
const shots = [
|
||||
['spec', `${base}/spec/`],
|
||||
['stack', `${base}/stack/`],
|
||||
['auto', 'https://auto.levkin.ca'],
|
||||
['caseware', 'https://caseware.levkin.ca'],
|
||||
['iliadobkin', 'https://iliadobkin.com'],
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
import { chromium } from 'playwright';
|
||||
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await browser.newPage({ viewport: { width: 1280, height: 800 } });
|
||||
await page.goto('http://localhost:5173/stack-folder/', { waitUntil: 'networkidle' });
|
||||
|
||||
for (const y of [0, 400, 800, 1200]) {
|
||||
await page.evaluate((sy) => window.scrollTo(0, sy), y);
|
||||
await page.waitForTimeout(150);
|
||||
const data = await page.evaluate(() => {
|
||||
return [...document.querySelectorAll('.folder')].map((f, i) => {
|
||||
const cs = getComputedStyle(f);
|
||||
const r = f.getBoundingClientRect();
|
||||
const body = f.querySelector('.body');
|
||||
const br = body.getBoundingClientRect();
|
||||
const sec = f.closest('.scroll-section');
|
||||
const sr = sec.getBoundingClientRect();
|
||||
return {
|
||||
i,
|
||||
folderTop: Math.round(r.top),
|
||||
bodyTop: Math.round(br.top),
|
||||
position: cs.position,
|
||||
top: cs.top,
|
||||
zIndex: f.style.zIndex || cs.zIndex,
|
||||
sectionTop: Math.round(sr.top),
|
||||
sectionBottom: Math.round(sr.bottom),
|
||||
sectionH: Math.round(sr.height),
|
||||
};
|
||||
});
|
||||
});
|
||||
console.log('scroll', y, JSON.stringify(data.filter((d) => d.sectionBottom > 0 && d.sectionTop < 800), null, 2));
|
||||
}
|
||||
|
||||
await browser.close();
|
||||
@@ -1,50 +0,0 @@
|
||||
import { chromium } from 'playwright';
|
||||
|
||||
const BASE = process.env.BASE_URL || 'http://localhost:5173';
|
||||
const REVEAL = 48 + 44 * 6 + 32; // approx px
|
||||
|
||||
async function testCover(page, path, bodySel, getTitle) {
|
||||
await page.goto(BASE + path, { waitUntil: 'networkidle' });
|
||||
const results = [];
|
||||
for (const y of [0, 600, 1200, 1800]) {
|
||||
await page.evaluate((sy) => window.scrollTo(0, sy), y);
|
||||
await page.waitForTimeout(250);
|
||||
const r = await page.evaluate(({ bodySel, reveal }) => {
|
||||
const bodies = [...document.querySelectorAll(bodySel)];
|
||||
const stacked = bodies.filter((b) => {
|
||||
const br = b.getBoundingClientRect();
|
||||
return Math.abs(br.top - reveal) < 30 && br.height > 100;
|
||||
});
|
||||
const top = stacked.sort((a, b) => {
|
||||
const az = parseInt(getComputedStyle(a.closest('.folder, .layer, .frame, .unit') || a).zIndex) || 0;
|
||||
const bz = parseInt(getComputedStyle(b.closest('.folder, .layer, .frame, .unit') || b).zIndex) || 0;
|
||||
return bz - az;
|
||||
})[0];
|
||||
const el = document.elementFromPoint(innerWidth / 2, reveal + 120);
|
||||
const hit = el?.closest('.folder, .layer, .frame, .unit');
|
||||
const layer = hit?.closest('[data-layer]')?.dataset?.layer ?? hit?.className?.match(/f(\d)|layer-(\d)|u(\d)/)?.[1];
|
||||
return {
|
||||
scrollY: scrollY,
|
||||
stackedCount: stacked.length,
|
||||
topZ: top ? getComputedStyle(top.closest('.folder, .layer, .frame, .unit')).zIndex : null,
|
||||
hitLayer: layer,
|
||||
hitText: hit?.querySelector('h1, h2, strong')?.textContent?.slice(0, 40),
|
||||
};
|
||||
}, { bodySel, reveal: REVEAL });
|
||||
results.push(r);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await browser.newPage({ viewport: { width: 1280, height: 800 } });
|
||||
|
||||
const folder = await testCover(page, '/stack-folder/', '.folder .body', (b) => b.querySelector('h1,h2')?.textContent);
|
||||
console.log('folder', JSON.stringify(folder, null, 2));
|
||||
|
||||
await page.screenshot({ path: '/tmp/levkin-cover-folder-1200.png' });
|
||||
await page.evaluate(() => window.scrollTo(0, 1200));
|
||||
await page.waitForTimeout(300);
|
||||
await page.screenshot({ path: '/tmp/levkin-cover-folder-1200b.png' });
|
||||
|
||||
await browser.close();
|
||||
@@ -1,118 +0,0 @@
|
||||
/**
|
||||
* Automated scroll/blur tests for stack-folder.
|
||||
* Run: node scripts/test-stack-scroll.mjs
|
||||
*/
|
||||
import { chromium } from 'playwright';
|
||||
|
||||
const URL = process.env.STACK_URL || 'http://localhost:5173/stack-folder/';
|
||||
const VIEWPORT = { width: 1280, height: 800 };
|
||||
|
||||
function fail(msg) {
|
||||
console.error('FAIL:', msg);
|
||||
process.exitCode = 1;
|
||||
}
|
||||
|
||||
function pass(msg) {
|
||||
console.log('PASS:', msg);
|
||||
}
|
||||
|
||||
async function readState(page) {
|
||||
return page.evaluate(() => {
|
||||
const stick =
|
||||
parseFloat(getComputedStyle(document.documentElement).fontSize) * 3;
|
||||
const l7Tab = document.querySelector('.f7 .tab');
|
||||
const l0 = document.querySelector('.f0');
|
||||
const l0Body = document.querySelector('.f0 .body');
|
||||
const folderBlur = l0?.style.getPropertyValue('--stack-blur') || '';
|
||||
const filter = l0Body ? getComputedStyle(l0Body).filter : '';
|
||||
const blurMatch = filter.match(/blur\(([\d.]+)px\)/);
|
||||
const blurPx = blurMatch ? parseFloat(blurMatch[1]) : 0;
|
||||
return {
|
||||
scrollY: window.scrollY,
|
||||
docMax: document.documentElement.scrollHeight - innerHeight,
|
||||
l7TabTop: l7Tab?.getBoundingClientRect().top ?? null,
|
||||
stick,
|
||||
l0Covered: l0?.classList.contains('is-covered'),
|
||||
l0BlurPx: blurPx,
|
||||
folderBlur,
|
||||
l0Filter: filter,
|
||||
runway: getComputedStyle(document.querySelector('.mount')).getPropertyValue(
|
||||
'--stack-runway',
|
||||
),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const browser = await chromium.launch();
|
||||
const page = await browser.newPage({ viewport: VIEWPORT });
|
||||
await page.goto(URL, { waitUntil: 'networkidle' });
|
||||
await page.waitForTimeout(400);
|
||||
|
||||
const top = await readState(page);
|
||||
if (top.l0Covered || top.l0BlurPx > 0.1) {
|
||||
fail(`L0 blurred at top (covered=${top.l0Covered}, blur=${top.l0BlurPx})`);
|
||||
} else {
|
||||
pass('L0 clear at scroll top');
|
||||
}
|
||||
|
||||
await page.evaluate(() => window.scrollTo(0, 999999));
|
||||
await page.waitForTimeout(350);
|
||||
const end = await readState(page);
|
||||
|
||||
if (end.l7TabTop === null) fail('L7 tab missing');
|
||||
else if (Math.abs(end.l7TabTop - end.stick) > 8) {
|
||||
fail(`L7 tab not on stick: top=${end.l7TabTop} stick=${end.stick} scrollY=${end.scrollY}`);
|
||||
} else {
|
||||
pass(`L7 on stick at max scroll (y=${end.scrollY}, tabTop=${end.l7TabTop.toFixed(1)})`);
|
||||
}
|
||||
|
||||
if (end.l0BlurPx < 2) {
|
||||
fail(`L0 not blurred when stacked (blur=${end.l0BlurPx})`);
|
||||
} else {
|
||||
pass(`L0 blurred when stacked (blur=${end.l0BlurPx}px)`);
|
||||
}
|
||||
|
||||
const midY = Math.floor(end.scrollY * 0.45);
|
||||
await page.evaluate((y) => window.scrollTo(0, y), midY);
|
||||
await page.waitForTimeout(200);
|
||||
const mid = await readState(page);
|
||||
if (mid.l0BlurPx > 2) {
|
||||
fail(`L0 still heavily blurred mid-scroll out (blur=${mid.l0BlurPx} at y=${mid.scrollY})`);
|
||||
} else {
|
||||
pass(`L0 unfades when scrolling out (blur=${mid.l0BlurPx}px at y=${mid.scrollY})`);
|
||||
}
|
||||
|
||||
await page.evaluate(() => window.scrollTo(0, 0));
|
||||
await page.waitForTimeout(350);
|
||||
const back = await readState(page);
|
||||
if (back.l0Covered || back.l0BlurPx > 0.1) {
|
||||
fail(`L0 still blurred after scroll to top (covered=${back.l0Covered}, blur=${back.l0BlurPx})`);
|
||||
} else {
|
||||
pass('L0 unfades after scroll back to top');
|
||||
}
|
||||
|
||||
const maxY = end.scrollY;
|
||||
await page.evaluate((y) => window.scrollTo(0, y), maxY);
|
||||
await page.waitForTimeout(200);
|
||||
await page.evaluate(() => window.scrollTo(0, 0));
|
||||
await page.waitForTimeout(350);
|
||||
const back2 = await readState(page);
|
||||
if (back2.l0Covered || back2.l0BlurPx > 0.1) {
|
||||
fail(`L0 stuck after down-up cycle (blur=${back2.l0BlurPx})`);
|
||||
} else {
|
||||
pass('L0 unfades after full down-up cycle');
|
||||
}
|
||||
|
||||
await browser.close();
|
||||
if (process.exitCode) {
|
||||
console.log('\nTests failed.');
|
||||
process.exit(1);
|
||||
}
|
||||
console.log('\nAll tests passed.');
|
||||
}
|
||||
|
||||
main().catch((e) => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -1,76 +0,0 @@
|
||||
import { chromium } from 'playwright';
|
||||
import { writeFileSync, mkdirSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
const BASE = process.env.BASE_URL || 'http://localhost:5173';
|
||||
const OUT = '/tmp/levkin-stack-test';
|
||||
mkdirSync(OUT, { recursive: true });
|
||||
|
||||
const pages = [
|
||||
{ name: 'stack', path: '/stack/', bodySel: '.layer-inner' },
|
||||
{ name: 'stack-folder', path: '/stack-folder/', bodySel: '.folder .body' },
|
||||
{ name: 'stack-trace', path: '/stack-trace/', bodySel: '.frame-body' },
|
||||
{ name: 'stack-rack', path: '/stack-rack/', bodySel: '.unit-body' },
|
||||
];
|
||||
|
||||
const scrollY = [0, 400, 800, 1200, 1800, 2400];
|
||||
|
||||
async function analyze(page, bodySel) {
|
||||
const reveal = await page.evaluate(() => {
|
||||
const s = getComputedStyle(document.documentElement);
|
||||
return parseFloat(s.getPropertyValue('--stack-reveal')) ||
|
||||
(48 + 44 * 6 + 32);
|
||||
});
|
||||
|
||||
return page.evaluate(({ bodySel, reveal }) => {
|
||||
const bodies = [...document.querySelectorAll(bodySel)];
|
||||
const stacked = bodies.filter((b) => {
|
||||
const r = b.getBoundingClientRect();
|
||||
return Math.abs(r.top - reveal) < 35 && r.height > 80;
|
||||
});
|
||||
const visible = bodies.filter((b) => {
|
||||
const r = b.getBoundingClientRect();
|
||||
return r.height > 80 && r.bottom > 0 && r.top < innerHeight;
|
||||
});
|
||||
const el = document.elementFromPoint(innerWidth / 2, reveal + 100);
|
||||
const panel = el?.closest('.folder, .layer, .frame, .unit');
|
||||
const layer = panel?.closest('[data-layer]')?.dataset?.layer;
|
||||
const title = panel?.querySelector('h1, h2, strong')?.textContent?.trim().slice(0, 50);
|
||||
return {
|
||||
scrollY: Math.round(window.scrollY),
|
||||
reveal: Math.round(reveal),
|
||||
stackedCount: stacked.length,
|
||||
visibleCount: visible.length,
|
||||
topLayer: layer,
|
||||
topTitle: title,
|
||||
issues: [],
|
||||
};
|
||||
}, { bodySel, reveal });
|
||||
}
|
||||
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const report = [];
|
||||
|
||||
for (const { name, path, bodySel } of pages) {
|
||||
const page = await browser.newPage({ viewport: { width: 1280, height: 800 } });
|
||||
await page.goto(BASE + path, { waitUntil: 'networkidle' });
|
||||
const shots = [];
|
||||
for (const y of scrollY) {
|
||||
await page.evaluate((sy) => window.scrollTo(0, sy), y);
|
||||
await page.waitForTimeout(250);
|
||||
const data = await analyze(page, bodySel);
|
||||
if (data.stackedCount < 2 && y > 200) data.issues.push('not stacking (need 2+ bodies at reveal line)');
|
||||
if (data.visibleCount > 2 && y > 200) data.issues.push(`${data.visibleCount} bodies spread out, not covering`);
|
||||
await page.screenshot({ path: join(OUT, `${name}-scroll-${y}.png`) });
|
||||
shots.push({ y, ...data });
|
||||
}
|
||||
report.push({ name, shots });
|
||||
await page.close();
|
||||
}
|
||||
|
||||
await browser.close();
|
||||
writeFileSync(join(OUT, 'report.json'), JSON.stringify(report, null, 2));
|
||||
console.log(JSON.stringify(report.map((r) => ({
|
||||
variant: r.name,
|
||||
checks: r.shots.map((s) => ({ y: s.y, stacked: s.stackedCount, top: s.topTitle, issues: s.issues })),
|
||||
})), null, 2));
|
||||
Reference in New Issue
Block a user