diff --git a/frontend/src/editor/editor.css b/frontend/src/editor/editor.css index 2f8d224..57965f9 100644 --- a/frontend/src/editor/editor.css +++ b/frontend/src/editor/editor.css @@ -8,10 +8,10 @@ --ne-text: #1f2328; --ne-muted: #656d76; --ne-faint: #afb8c1; - --ne-border: #d0d7de; + --ne-border: #ded8cd; --ne-accent: #0969da; - --ne-surface: #ffffff; - --ne-subtle: #f6f8fa; + --ne-surface: #fbfaf7; + --ne-subtle: #f2efe8; } .note-editor[data-theme="dark"] { --ne-text: #e6edf3; @@ -19,20 +19,28 @@ --ne-faint: #484f58; --ne-border: #30363d; --ne-accent: #388bfd; - --ne-surface: #0d1117; - --ne-subtle: #161b22; + --ne-surface: #161c24; + --ne-subtle: #1d242e; } .note-editor .ProseMirror { outline: none; color: var(--ne-text); font-family: "Newsreader", Georgia, serif; - font-size: 15px; - line-height: 1.7; - padding: 20px 24px; + font-size: 16px; + line-height: 1.75; + /* A margin, the way a page has one. The old 24px let the text run to the very + edge of the sheet, which is the one thing paper never does. */ + padding: 44px 56px 64px; min-height: 100%; word-break: break-word; } + +@media (max-width: 767px) { + .note-editor .ProseMirror { + padding: 22px 20px 40px; + } +} .note-editor .ProseMirror > * + * { margin-top: 0.6em; } diff --git a/frontend/src/export/print.ts b/frontend/src/export/print.ts index 77780bf..9bfc113 100644 --- a/frontend/src/export/print.ts +++ b/frontend/src/export/print.ts @@ -158,7 +158,7 @@ function articleFor(note: PrintableNote): string {

${esc(note.meta.title || "Untitled")}

${when ? `
${esc(when)}
` : ""} ${body} - ${note.drawing ? `
Zeichnung
` : ""} + ${note.drawing ? `
Drawing
` : ""} `; } diff --git a/frontend/src/features/notes/NoteEditor.jsx b/frontend/src/features/notes/NoteEditor.jsx index ccca1fe..4647696 100644 --- a/frontend/src/features/notes/NoteEditor.jsx +++ b/frontend/src/features/notes/NoteEditor.jsx @@ -75,6 +75,15 @@ export function NoteEditor({ const T = useMemo( () => ({ bg: dark ? "#0d1117" : "#ffffff", + /* The sheet. + * + * In daylight it is warm off-white — plain white next to a white toolbar + * is not a sheet, it is an absence of one. At night it is a raised surface + * rather than a white rectangle: a page that glows brighter than the room + * is what makes an app unusable in the evening, and "paper" in a dark room + * still means "the surface I write on", not "white". */ + paper: dark ? "#1b2430" : "#fbfaf7", + paperEdge: dark ? "#333f4e" : "#e4dfd5", toolbar: dark ? "#161b22" : "#f6f8fa", border: dark ? "#30363d" : "#d0d7de", text: dark ? "#e6edf3" : "#1f2328", @@ -154,7 +163,13 @@ export function NoteEditor({ const mobile = window.innerWidth < 768; setIsMobileViewport(mobile); const sidePad = mobile ? 10 : 28; - const desktopMax = 760; + /* How wide a sheet gets. + * + * With 56px of inner margin this leaves a text column of about 68 + * characters at the body size — measured, not guessed. The old 760 ran to + * nearly 90, past the width at which the eye reliably finds the start of + * the next line, and the single biggest reason the surface read badly. */ + const desktopMax = 620; const minW = mobile ? 220 : 320; const maxAllowed = Math.max(220, W - sidePad * 2); const targetW = mobile ? maxAllowed : Math.min(desktopMax, maxAllowed); @@ -445,11 +460,9 @@ 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%)", + // The desk the sheet lies on, a touch darker than the sheet so the + // shadow has something to fall onto. + background: isMobileViewport ? T.paper : dark ? "#0b0f14" : "#eceae5", }} > {/* Main toolbar */} @@ -660,11 +673,18 @@ export function NoteEditor({ position: "relative", width: stageSize.w, minHeight: stageSize.h, - margin: isMobileViewport ? "0 auto" : "0 0 0 28px", - background: T.bg, - border: isMobileViewport ? "none" : `1px solid ${T.border}`, - borderRadius: isMobileViewport ? 0 : 14, - boxShadow: isMobileViewport ? "none" : "0 16px 36px rgba(0,0,0,0.14)", + // Centred. It used to sit 28px from the left and stop short of the + // right, so the margins were visibly unequal. + margin: "0 auto", + background: T.paper, + border: isMobileViewport ? "none" : `1px solid ${T.paperEdge}`, + borderRadius: isMobileViewport ? 0 : 3, + // Paper lies on something; a sheet with no shadow is a panel. + boxShadow: isMobileViewport + ? "none" + : dark + ? "0 10px 30px rgba(0,0,0,0.45)" + : "0 10px 28px rgba(60,50,30,0.13)", }} >
startDrag(e, pin)} onPointerMove={onPointerMove} onPointerUp={(e) => endDrag(e, pin)} diff --git a/frontend/src/import/zipRead.test.ts b/frontend/src/import/zipRead.test.ts index 4f3bfc3..7ca0279 100644 --- a/frontend/src/import/zipRead.test.ts +++ b/frontend/src/import/zipRead.test.ts @@ -40,7 +40,7 @@ describe("readZip", () => { }); it("refuses something that is not a ZIP", async () => { - await expect(readZip(new TextEncoder().encode("nur Text").buffer)).rejects.toThrow(/keine ZIP/); + await expect(readZip(new TextEncoder().encode("nur Text").buffer)).rejects.toThrow(/not a ZIP/); }); it("returns an empty list for an archive with no entries", async () => { diff --git a/frontend/src/import/zipRead.ts b/frontend/src/import/zipRead.ts index a1e5411..21c733a 100644 --- a/frontend/src/import/zipRead.ts +++ b/frontend/src/import/zipRead.ts @@ -35,7 +35,7 @@ function findEocd(view: DataView): number { async function inflateRaw(bytes: Uint8Array): Promise { if (typeof DecompressionStream === "undefined") { - throw new Error("Dieses Archiv ist komprimiert; der Browser kann es nicht entpacken."); + throw new Error("This archive is compressed and the browser cannot unpack it."); } const copy = new Uint8Array(bytes.byteLength); copy.set(bytes); @@ -53,7 +53,7 @@ export async function readZip(buffer: ArrayBuffer): Promise { const all = new Uint8Array(buffer); const eocd = findEocd(view); - if (eocd < 0) throw new Error("Das ist keine ZIP-Datei."); + if (eocd < 0) throw new Error("That is not a ZIP file."); const count = view.getUint16(eocd + 10, true); let at = view.getUint32(eocd + 16, true); diff --git a/frontend/src/ui/AccountMenu.jsx b/frontend/src/ui/AccountMenu.jsx index 3fccaa7..e386e19 100644 --- a/frontend/src/ui/AccountMenu.jsx +++ b/frontend/src/ui/AccountMenu.jsx @@ -386,7 +386,7 @@ export function AccountMenu({ user, onClose, onLogout, onUserUpdate, onOpenAdmin marginBottom: 8, }} > - {I.export} Alle Notizen exportieren + {I.export} Export all notes {user?.isAdmin && (
+
Reading the note…
) : parts === null ? ( stepParts() ) : ( diff --git a/readme.md b/readme.md index af3024a..406e992 100644 --- a/readme.md +++ b/readme.md @@ -368,6 +368,33 @@ state of their own, so they can be read on their own. --- +## The writing surface + +A real sheet, lying on a desk. It had been a page-*shaped* box that was not +page-*coloured* — the same fill as everything around it with a hairline border, +so it read as an empty panel rather than as paper. + +- **Warm off-white in daylight** (`#fbfaf7`). Plain white next to a white toolbar + is not a sheet, it is the absence of one. +- **A raised surface at night** (`#1b2430`), lighter than the desk, with an edge + and a shadow — deliberately not a white rectangle. A page that glows brighter + than the room is what makes an app unusable in the evening; in a dark theme + "paper" means *the surface I write on*, not *white*. +- **About 73 characters to the line**, measured rather than guessed: 620px of + sheet with a 56px margin. It ran to 84 before, past the width at which the eye + reliably finds the start of the next line, and that was the single biggest + reason the surface read badly. +- **Centred**, with almost square corners. It used to sit 28px from the left and + stop short of the right, and carried a 14px radius that belongs to a dialog. + +Blocks *on* the sheet — quotes, code, table headers — are tinted from paper +rather than from the app background, or they would read as sitting underneath it. + +The sheet keeps the drawing's page proportion, so a short note leaves an empty +page below it. That is what a sheet of paper does, and it is the price of having +one: the canvas needs a fixed page for a stroke to land in the same place on +every screen. + ## Interface language English throughout. It had drifted into two languages — the frame in English,