Chapters as rows, in two views, in their own colours
Three things Marc asked for, and one of them turned out to be the hinge
the other two hang from.
**A chapter is a row like any other.** Same grip, same drag, same place
in a hand-made arrangement as a note or a separator. That needed notes
and chapters numbered from *one* counter: numbered separately they are
two sequences that cannot be interleaved again, and a chapter dragged
between two notes springs back to the top. Positions therefore have to be
named rather than read off an array index, so POST /notes/reorder now
takes `positions: [{id, order}]` beside the older `ids`, and chapters
have a reorder route of their own.
**Two ways for a chapter to open**, chosen per account beside the
typeface, for the same reason that one lives there: it is a choice about
how your own notes are laid out and it should follow you to the phone.
Beside the list, its notes in a further column the way OneNote lays out
pages — the notebook's own list carrying on to the left, chapter rows and
all. Or unfolded under the chapter's own row, indented against a rule in
its colour. A phone has room for neither and fills the list instead, with
a way back, which is what it did before there was a choice.
**Its own colour**, from eight named ones rather than the full palette:
the point of chapters is that they are different places, and twenty
near-identical blues do not help anyone tell them apart.
The colour is validated on the way in — it is written by a browser, so
only a plain #rrggbb gets through and anything else becomes no colour.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6cad632a0c
commit
d74fea4905
12 files changed
+640
-88
No files matched your search
@@ -4,13 +4,15 @@ import { I } from "./icons.jsx";
|
||||
|
||||
/* ═══════ ACCOUNT MENU ═══════ */
|
||||
export function AccountMenu({ user, onClose, onLogout, onUserUpdate, onOpenAdmin, onExportAll, dark }) {
|
||||
const chooseFont = async (noteFont) => {
|
||||
const choosePreference = async (change) => {
|
||||
try {
|
||||
onUserUpdate(await sync.updatePreferences({ noteFont }));
|
||||
onUserUpdate(await sync.updatePreferences(change));
|
||||
} catch {
|
||||
/* the choice is presentation; failing to store it must not block the panel */
|
||||
}
|
||||
};
|
||||
const chooseFont = (noteFont) => choosePreference({ noteFont });
|
||||
const chooseChapterView = (chapterView) => choosePreference({ chapterView });
|
||||
const [tab, setTab] = useState("profile"); // profile | username | password
|
||||
const [displayName, setDisplayName] = useState(user?.displayName || "");
|
||||
const [newUsername, setNewUsername] = useState(user?.username || "");
|
||||
@@ -305,6 +307,47 @@ export function AccountMenu({ user, onClose, onLogout, onUserUpdate, onOpenAdmin
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* How a chapter opens. Kept beside the typeface because both are
|
||||
the same kind of thing: how your own notes are laid out, stored
|
||||
on the account so the answer follows you to the phone. */}
|
||||
<div style={{ marginTop: 18 }}>
|
||||
<label style={{ fontSize: 12, color: T.muted, marginBottom: 6, display: "block" }}>
|
||||
Chapters open
|
||||
</label>
|
||||
<div style={{ display: "grid", gridTemplateColumns: "repeat(2, 1fr)", gap: 8 }}>
|
||||
{[
|
||||
["column", "Beside the list", "Its notes in a further column, the way OneNote lays out pages"],
|
||||
["inline", "Under the chapter", "Its notes unfold in place, below the chapter's own row"],
|
||||
].map(([value, label, hint]) => {
|
||||
const active = (user.preferences?.chapterView || "column") === value;
|
||||
return (
|
||||
<button
|
||||
key={value}
|
||||
data-chapter-view={value}
|
||||
onClick={() => chooseChapterView(value)}
|
||||
title={hint}
|
||||
style={{
|
||||
padding: "10px 8px",
|
||||
borderRadius: 10,
|
||||
border: `1.5px solid ${active ? T.accent : T.border}`,
|
||||
background: active ? T.accent + "15" : "transparent",
|
||||
color: active ? T.accent : T.text,
|
||||
cursor: "pointer",
|
||||
fontSize: 12.5,
|
||||
textAlign: "left",
|
||||
lineHeight: 1.35,
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div style={{ fontSize: 11, color: T.muted, marginTop: 6, lineHeight: 1.5 }}>
|
||||
A phone has room for neither, and fills the screen either way — with a way back.
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
|
||||
Reference in new issue
Block a user