Give the writing a page to sit on, so the empty half means something

The text was capped at a readable width inside a much wider pane, which
left a large void on the right with nothing to explain it. The page is
now a drawn surface: the writing sits on it with a margin, and the space
that was empty is that margin.

Page boundaries used to be a hairline. They are a dashed band now, with
the next page's number in the margin beside it, so a boundary reads as
one page ending rather than as a rule in the text.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
marcandClaude Opus 5 committed 2026-08-11 18:21:26 +02:00
1 parent fed53772aa
commit 7910179f29
1 file changed
+52 -28
+52 -28
View File
@@ -100,6 +100,10 @@ export function NoteEditor({
muted: dark ? "#8b949e" : "#656d76",
active: dark ? "#388bfd" : "#0969da",
faint: dark ? "#484f58" : "#afb8c1",
/* A shade apart from the desk it lies on — enough to read as a surface,
not so much that it glares in a dark room. */
pageBg: dark ? "#131922" : "#ffffff",
desk: dark ? "#0b0f14" : "#e9ecef",
}),
[dark]
);
@@ -498,11 +502,7 @@ export function NoteEditor({
* width a property of the window, which is what it always looked like. */
flex: 1,
minWidth: 0,
background: isMobileViewport
? T.bg
: dark
? "linear-gradient(180deg,#0f141b 0%,#0d1117 60%)"
: "linear-gradient(180deg,#f8fafd 0%,#eef3f8 70%)",
background: isMobileViewport ? T.pageBg : T.desk,
}}
>
{/* Main toolbar */}
@@ -716,41 +716,65 @@ export function NoteEditor({
padding: isMobileViewport ? "10px 0 18px" : "18px 0 28px",
}}
>
{/* The page is a surface, always.
With the text capped at a readable measure and nothing behind it,
the right 40% of the pane was simply void. A page gives that space
a meaning — it is the margin — and it is what lets "page 2" show as
a page rather than as a line across an empty field. */}
<div
style={{
position: "relative",
// Without a page the column is as wide as it needs to be to read
// comfortably, and centred; with one it is the canvas's page.
// The text caps its own width in the stylesheet, so this is only
// the page when there is one.
width: needsPage ? stageSize.w : "100%",
minHeight: needsPage ? stageSize.h : undefined,
margin: "0 auto",
background: needsPage ? T.bg : "transparent",
border: needsPage && !isMobileViewport ? `1px solid ${T.border}` : "none",
borderRadius: needsPage && !isMobileViewport ? 14 : 0,
boxShadow: needsPage && !isMobileViewport ? "0 16px 36px rgba(0,0,0,0.14)" : "none",
width: stageSize.w,
maxWidth: "100%",
minHeight: stageSize.h,
margin: isMobileViewport ? "0 auto" : "0 0 0 28px",
background: T.pageBg,
border: isMobileViewport ? "none" : `1px solid ${T.border}`,
borderRadius: isMobileViewport ? 0 : 6,
boxShadow: isMobileViewport ? "none" : "0 6px 20px rgba(0,0,0,0.16)",
}}
>
{/* A hairline where each page ends, so the count in the toolbar
corresponds to something you can see. Drawn behind the text and
deaf to the pointer — it marks the paper, it is not part of it. */}
{pages.count > 1 && mode === "text" && (
{/* Where one page ends and the next begins.
A band rather than a hairline, with the new page's number set in
the margin — the count in the toolbar should correspond to
something that reads as a page, not to a line across a field.
The text flows on through: the note is one document, and a real
page break would mean laying the text out ourselves. */}
{Array.from({ length: Math.max(0, pages.count - 1) }, (_, i) => (
<div
key={i}
aria-hidden
data-page-break={i + 2}
style={{
position: "absolute",
inset: 0,
left: 0,
right: 0,
top: stageSize.h * (i + 1),
height: 0,
pointerEvents: "none",
backgroundImage: `linear-gradient(to bottom, transparent calc(100% - 1px), ${T.border} 100%)`,
backgroundSize: `100% ${stageSize.h}px`,
opacity: 0.55,
borderTop: `1px dashed ${T.border}`,
}}
/>
)}
>
<span
style={{
position: "absolute",
right: 14,
top: 8,
fontSize: 10.5,
letterSpacing: 0.5,
color: T.faint,
background: T.pageBg,
padding: "0 4px",
}}
>
Page {i + 2}
</span>
</div>
))}
<div
style={{
minHeight: needsPage ? stageSize.h : undefined,
minHeight: stageSize.h,
width: "100%",
pointerEvents: mode === "text" ? "auto" : "none",
}}
@@ -759,7 +783,7 @@ export function NoteEditor({
editor={textEditor}
dark={dark}
font={noteFont}
minHeight={needsPage ? stageSize.h : 0}
minHeight={stageSize.h}
onOpenNote={onOpenNote}
resolveLinkLabel={resolveLinkLabel}
/>