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 ? `
` : ""}
+ ${note.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,