The same page on every screen

The text is laid out on a page of one fixed size and the sheet scaled to
the screen, so a phone breaks it exactly where a desktop does — twenty-one
lines to the page on both. The type size is computed from the page and the
line count rather than chosen.

A PDF's full screen is the browser window, not the screen, and its button
sits beside the zoom. "Recent Notes" is stamped by every write, not only by
a rename. And a PDF that will not open says which of the four reasons it is.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
marcandClaude Opus 5 committed 2026-08-26 13:23:03 +02:00
1 parent 9507761bce
commit 4013e959d6
12 files changed
+295 -101

No files matched your search

+70
View File
@@ -261,6 +261,76 @@ try {
"and the gap stays blank — no placeholder printed at the foot of the page"
);
/* ── twenty-one lines, and the same twenty-one everywhere ──
*
* Two claims. First, a page holds the number of lines a ruled page holds —
* it held eighteen on a phone, which is what started this. Second, and the
* one that matters: the phone breaks the text in exactly the same places the
* desktop does. It did not, because the text used to be set in screen pixels:
* a 340-pixel sheet fitted nothing like what a 760-pixel one did, so the same
* note had different lines on different pages depending on what you read it
* on. The text is now laid out on a page of a fixed size and the whole sheet
* scaled, so the only thing a narrow screen changes is how big it looks.
*
* Measured in page units — every length divided by the sheet's own scale —
* because screen pixels are precisely what must not agree. */
const pageUnits = () =>
p.evaluate(() => {
const scale = Number(document.querySelector("[data-text-page-scale]").dataset.textPageScale);
const pm = document.querySelector(".ProseMirror");
const cs = getComputedStyle(pm);
const sheet = document.querySelector("[data-page-sheet]").getBoundingClientRect();
const top = pm.getBoundingClientRect().top;
const blocks = [...pm.children]
.filter((el) => !el.classList.contains("ne-page-spacer"))
.slice(0, 40)
.map((el) => {
const r = el.getBoundingClientRect();
return { y: Math.round((r.top - top) / scale), h: Math.round(r.height / scale) };
});
return {
lines:
(sheet.height / scale - parseFloat(cs.paddingTop) - parseFloat(cs.paddingBottom)) /
parseFloat(cs.lineHeight),
blocks,
};
});
const onDesktop = await pageUnits();
check(
Math.floor(onDesktop.lines) === 21,
"a page holds twenty-one lines",
`${onDesktop.lines.toFixed(2)} fit`
);
const phone = { width: 390, height: 844 };
const desktop = p.viewportSize();
await p.setViewportSize(phone);
await p.waitForTimeout(2000);
/* A phone shows one pane at a time, so the note may have been left behind in
the list when the window narrowed. Open it again before measuring. */
if ((await p.locator("[data-text-page-scale]").count()) === 0) await reopenNote(p, "Langer Text");
await p.waitForSelector("[data-text-page-scale]", { timeout: 15000 });
await p.waitForTimeout(3000);
const onPhone = await pageUnits();
check(
Math.floor(onPhone.lines) === 21,
"on a phone as well — the same page, not a smaller one",
`${onPhone.lines.toFixed(2)} fit`
);
const moved = onDesktop.blocks
.map((b, i) => ({ i, b, o: onPhone.blocks[i] }))
.filter(({ b, o }) => !o || Math.abs(b.y - o.y) > 1 || Math.abs(b.h - o.h) > 1)
.map(({ i, b, o }) => `#${i}: ${b.y}/${b.h} vs ${o ? `${o.y}/${o.h}` : "missing"}`);
check(
onPhone.blocks.length === onDesktop.blocks.length && moved.length === 0,
"and every paragraph falls in the same place on both, to the page unit",
moved.slice(0, 4).join(" | ")
);
await p.setViewportSize(desktop);
await p.waitForTimeout(2000);
if ((await p.locator(".ProseMirror").count()) === 0) await reopenNote(p, "Langer Text");
/* The note is still one document — pages are drawn, not stored. */
const stored = (await api(p, "GET", "/notes")).body[0];
const body = (await api(p, "GET", `/notes/${stored.id}`)).body.content;