A chapter you can see, in the colour you chose
A chapter did not show until the notebook had a note in it. "Nothing here" was asked of the notes alone, so a notebook whose only contents were chapters showed the empty-list panel with the chapters hidden behind it — making a chapter in a fresh notebook appeared to do nothing at all. It is asked of every row now, and a notebook with chapters but no loose notes says so in one line rather than looking like a list that failed to load. The colour is chosen while the chapter is being made, in the dialog that asks for its name. It is what tells one chapter from the next, and having to right-click a fresh one to say which it is meant two visits for one decision. Renaming and recolouring afterwards are one entry in its menu and one write; the server already took the colour on create, the client simply never sent it. The word "here" is gone. The line along the top of the pane says which of the two places you are in, and saying it twice was noise. Thirteen suites green: 338 browser checks, 241 unit tests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ebf1f1bc62
commit
165ccf3cf7
8 files changed
+197
-66
No files matched your search
@@ -8,6 +8,28 @@ not from the output of `docker push`.
|
||||
|
||||
---
|
||||
|
||||
## 2026-08-12 — A chapter you can see, in the colour you chose
|
||||
|
||||
**Images** — web `sha256:…`, server unchanged at `sha256:ed0b9970…`
|
||||
|
||||
**Fixed**
|
||||
- **A chapter did not show until the notebook had a note in it.** "Nothing here"
|
||||
was asked of the notes alone, so a notebook whose only contents were chapters
|
||||
showed the empty-list panel with the chapters hidden behind it — making a
|
||||
chapter in a fresh notebook appeared to do nothing at all. It is now asked of
|
||||
every row, and a notebook with chapters but no loose notes says so in one line
|
||||
rather than looking like a list that failed to load.
|
||||
|
||||
**Changed**
|
||||
- **A chapter's colour is chosen while it is being made**, in the same dialog as
|
||||
its name. It is what tells one chapter from the next, and having to right-click
|
||||
a fresh one to say which it is meant two visits for one decision. Renaming and
|
||||
recolouring later are one entry in its menu, and one write.
|
||||
- **The word *here* is gone.** The line along the top of the pane says which of
|
||||
the two places you are in; saying it twice was noise.
|
||||
|
||||
---
|
||||
|
||||
## 2026-08-12 — A filed note stays filed
|
||||
|
||||
**Images** — web `sha256:2041f103…`, server `sha256:ed0b9970…`
|
||||
|
||||
+30
-1
@@ -24,10 +24,14 @@ try {
|
||||
|
||||
const titles = () =>
|
||||
p.locator("[data-note-row] [data-note-title]").evaluateAll((els) => els.map((e) => e.textContent.trim()));
|
||||
const addChapter = async (name) => {
|
||||
/* The colour is chosen while the chapter is being made. It is what tells one
|
||||
chapter from the next, and having to right-click a fresh one to say which
|
||||
it is meant two visits for a single decision. */
|
||||
const addChapter = async (name, colour = null) => {
|
||||
await p.locator("[data-add-chapter]").click();
|
||||
await p.waitForTimeout(600);
|
||||
await p.getByLabel("Chapter name").fill(name);
|
||||
if (colour) await p.locator(`[data-colour-option="${colour}"]`).click();
|
||||
await p.getByRole("button", { name: "Create", exact: true }).click();
|
||||
await p.waitForTimeout(2000);
|
||||
};
|
||||
@@ -66,6 +70,31 @@ try {
|
||||
const server = (await api(p, "GET", "/chapters")).body;
|
||||
check(server.length === 1 && server[0].name === "Rechnungen", "the server has it", JSON.stringify(server));
|
||||
|
||||
/* ── in an empty notebook too ──
|
||||
* "Nothing here" was asked of the notes alone, so a notebook whose only
|
||||
* contents were chapters showed the empty-list panel with the chapters hidden
|
||||
* behind it: making a chapter in a fresh notebook appeared to do nothing. */
|
||||
await createNotebook(p, "Leeres Buch");
|
||||
await p.locator("button").filter({ hasText: "Leeres Buch" }).first().click();
|
||||
await p.waitForTimeout(1000);
|
||||
await addChapter("Nur ein Kapitel", "#a371f7");
|
||||
check(
|
||||
(await p.locator("[data-chapter-row]").count()) === 1,
|
||||
"a chapter shows in a notebook that has no notes at all",
|
||||
String(await p.locator("[data-chapter-row]").count())
|
||||
);
|
||||
const madeWithColour = (await api(p, "GET", "/chapters")).body.find((c) => c.name === "Nur ein Kapitel");
|
||||
check(
|
||||
madeWithColour?.color === "#a371f7",
|
||||
"and the colour picked while making it is stored with it",
|
||||
String(madeWithColour?.color)
|
||||
);
|
||||
check(
|
||||
(await p.locator("[data-no-loose-notes]").count()) === 1,
|
||||
"and the list says why it is otherwise empty rather than looking broken"
|
||||
);
|
||||
await openNotebook();
|
||||
|
||||
/* ── filing a note ── */
|
||||
check(
|
||||
(await p.locator("[data-move-note]").count()) === 0,
|
||||
|
||||
+18
-15
@@ -640,9 +640,17 @@ export default function App() {
|
||||
setInChapter(!!next);
|
||||
};
|
||||
|
||||
/* Named and coloured in one go. The colour is what tells one chapter from the
|
||||
next, so asking for it while the chapter is being made is one decision
|
||||
rather than a second visit through the right-click menu. */
|
||||
const addChapter = () => {
|
||||
if (!activeNotebook) return;
|
||||
setNamingChapter({ title: "New chapter", value: "", onSave: (name) => data.createChapter(activeNotebook.id, name) });
|
||||
setNamingChapter({
|
||||
title: "New chapter",
|
||||
value: "",
|
||||
color: null,
|
||||
onSave: (name, color) => data.createChapter(activeNotebook.id, name, color),
|
||||
});
|
||||
};
|
||||
|
||||
const chapterContextMenu = (e, chapter) => {
|
||||
@@ -653,24 +661,25 @@ export default function App() {
|
||||
y: e.clientY,
|
||||
items: [
|
||||
{
|
||||
label: "Rename",
|
||||
label: "Rename or recolour",
|
||||
icon: I.edit,
|
||||
action: () =>
|
||||
setNamingChapter({
|
||||
title: "Rename chapter",
|
||||
title: "Chapter",
|
||||
value: chapter.name,
|
||||
onSave: (name) => data.renameChapter(chapter.id, name),
|
||||
color: chapter.color ?? null,
|
||||
onSave: (name, color) => data.editChapter(chapter.id, { name, color }),
|
||||
}),
|
||||
},
|
||||
{
|
||||
label: "Colour",
|
||||
icon: I.edit,
|
||||
submenu: [
|
||||
{ label: "None", colorDot: T.faint, action: () => data.recolourChapter(chapter.id, null) },
|
||||
{ label: "None", colorDot: T.faint, action: () => data.editChapter(chapter.id, { color: null }) },
|
||||
...CHAPTER_COLORS.map((c) => ({
|
||||
label: c.label,
|
||||
colorDot: c.value,
|
||||
action: () => data.recolourChapter(chapter.id, c.value),
|
||||
action: () => data.editChapter(chapter.id, { color: c.value }),
|
||||
})),
|
||||
],
|
||||
},
|
||||
@@ -769,11 +778,6 @@ export default function App() {
|
||||
paddingLeft: 8,
|
||||
}}
|
||||
>
|
||||
{inChapter && (
|
||||
<div style={{ fontSize: 10, color: T.accent, fontWeight: 700, padding: "2px 10px 4px" }}>
|
||||
· new notes land here
|
||||
</div>
|
||||
)}
|
||||
{chapterNotes.length === 0 && (
|
||||
<div style={{ padding: "8px 10px", fontSize: 12, color: T.muted }}>Nothing in here yet.</div>
|
||||
)}
|
||||
@@ -984,9 +988,6 @@ export default function App() {
|
||||
>
|
||||
{openChapter.name}
|
||||
</span>
|
||||
{inChapter && (
|
||||
<span style={{ fontSize: 10, color: T.accent, fontWeight: 700, flexShrink: 0 }}>· here</span>
|
||||
)}
|
||||
<button
|
||||
onClick={() => {
|
||||
setActiveChapter(null);
|
||||
@@ -1174,7 +1175,9 @@ export default function App() {
|
||||
title={namingChapter.title}
|
||||
value={namingChapter.value}
|
||||
label="Chapter name"
|
||||
confirmLabel={namingChapter.value ? "Rename" : "Create"}
|
||||
colors={CHAPTER_COLORS}
|
||||
color={namingChapter.color}
|
||||
confirmLabel={namingChapter.value ? "Save" : "Create"}
|
||||
onSave={namingChapter.onSave}
|
||||
onClose={() => setNamingChapter(null)}
|
||||
dark={dark}
|
||||
|
||||
@@ -498,6 +498,16 @@ export function NoteList({
|
||||
a way back. In the other two views the chapter rows stay where they are. */
|
||||
const shownChapters = openChapter ? [] : chapters || [];
|
||||
|
||||
/* Every row this list shows, in the order it shows them.
|
||||
*
|
||||
* Separators are part of the manual arrangement and only mean something
|
||||
* there: in "newest first" the notes would slide past a line on every edit.
|
||||
* They are not shown in the other orders, and are still in place on the way
|
||||
* back. */
|
||||
const entries = reorderable
|
||||
? mergeEntries(notes, separators, shownChapters)
|
||||
: [...shownChapters.map(asChapterEntry), ...notes.map(asNoteEntry)];
|
||||
|
||||
return (
|
||||
<div
|
||||
data-list-current={isCurrent === null || isCurrent === undefined ? undefined : isCurrent ? "on" : "off"}
|
||||
@@ -560,11 +570,6 @@ export function NoteList({
|
||||
>
|
||||
{openChapter ? openChapter.name : title}
|
||||
</h2>
|
||||
{/* Said in words as well as in the line above, because a coloured edge
|
||||
is only a hint until you know what it means. */}
|
||||
{isCurrent && (
|
||||
<span style={{ fontSize: 10, color: T.accent, fontWeight: 700, flexShrink: 0 }}>· here</span>
|
||||
)}
|
||||
</div>
|
||||
<div style={{ display: "flex", gap: 4, flexShrink: 0 }}>
|
||||
{reorderable && (
|
||||
@@ -712,7 +717,11 @@ export function NoteList({
|
||||
onPointerDown={onFocusList}
|
||||
style={{ flex: 1, overflowY: "auto", padding: 6, WebkitOverflowScrolling: "touch" }}
|
||||
>
|
||||
{notes.length === 0 ? (
|
||||
{/* "Nothing here" means nothing at all — not "no loose notes".
|
||||
Asked of the notes alone, a notebook whose only contents were
|
||||
chapters showed an empty list with the chapters hidden behind it,
|
||||
and a chapter made in an empty notebook appeared to do nothing. */}
|
||||
{entries.length === 0 ? (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
@@ -755,14 +764,7 @@ export function NoteList({
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{/* Separators are part of the manual arrangement and only mean
|
||||
something there: in "newest first" the notes would slide past a
|
||||
line on every edit. They are not shown in the other orders, and
|
||||
are still in place on the way back. */}
|
||||
{(reorderable
|
||||
? mergeEntries(notes, separators, shownChapters)
|
||||
: [...shownChapters.map(asChapterEntry), ...notes.map(asNoteEntry)]
|
||||
).map((entry) => {
|
||||
{entries.map((entry) => {
|
||||
const showDropLine = moving && dnd.overId === entry.id;
|
||||
/* The whole row, indicator included, is the drop target.
|
||||
*
|
||||
@@ -890,6 +892,13 @@ export function NoteList({
|
||||
})}
|
||||
{/* Dropping below the last row moves something to the end, which
|
||||
otherwise needs an awkward drop onto the very last note. */}
|
||||
{/* Said out loud, so a notebook holding only chapters does not read
|
||||
as a list that failed to load. */}
|
||||
{notes.length === 0 && !isTrashView && (
|
||||
<div data-no-loose-notes style={{ padding: "12px 12px 4px", fontSize: 12, color: T.muted }}>
|
||||
No notes outside the chapters yet.
|
||||
</div>
|
||||
)}
|
||||
{moving && (
|
||||
<div data-drop-end {...dnd.rowProps(END_ID)} style={{ minHeight: 56 }}>
|
||||
{dnd.overId === END_ID && <DropLine T={T} />}
|
||||
|
||||
@@ -52,9 +52,9 @@ export interface NoteData {
|
||||
createNotebook: (fields: { name: string; cover: number }) => Promise<Notebook>;
|
||||
updateNotebook: (id: string, updates: Partial<Notebook>) => Promise<void>;
|
||||
chapters: Chapter[];
|
||||
createChapter: (notebookId: string, name: string) => Promise<Chapter>;
|
||||
renameChapter: (id: string, name: string) => Promise<void>;
|
||||
recolourChapter: (id: string, color: string | null) => Promise<void>;
|
||||
createChapter: (notebookId: string, name: string, color?: string | null) => Promise<Chapter>;
|
||||
/** Name, colour, or both — one write for one decision. */
|
||||
editChapter: (id: string, updates: { name?: string; color?: string | null }) => Promise<void>;
|
||||
deleteChapter: (id: string) => Promise<void>;
|
||||
deleteNotebook: (id: string) => Promise<void>;
|
||||
reorderNotebooks: (dragId: string, targetId: string) => void;
|
||||
@@ -356,20 +356,23 @@ export function useNoteData(user: User | null, activeNote: string | null): NoteD
|
||||
* having the removal applied by hand here.
|
||||
*/
|
||||
|
||||
const createChapter = useCallback(async (notebookId: string, name: string) => {
|
||||
const chapter = await sync.createChapter(notebookId, name);
|
||||
setChapters((p) => [...p, chapter]);
|
||||
return chapter;
|
||||
}, []);
|
||||
const createChapter = useCallback(
|
||||
async (notebookId: string, name: string, color: string | null = null) => {
|
||||
const chapter = await sync.createChapter(notebookId, name, color);
|
||||
setChapters((p) => [...p, chapter]);
|
||||
return chapter;
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const renameChapter = useCallback(async (id: string, name: string) => {
|
||||
setChapters((p) => p.map((c) => (c.id === id ? { ...c, name } : c)));
|
||||
await sync.updateChapter(id, { name });
|
||||
}, []);
|
||||
|
||||
const recolourChapter = useCallback(async (id: string, color: string | null) => {
|
||||
setChapters((p) => p.map((c) => (c.id === id ? { ...c, color } : c)));
|
||||
await sync.updateChapter(id, { color });
|
||||
/* Name and colour go the same way, in one write.
|
||||
*
|
||||
* They used to be two calls because they came from two places — a dialog and
|
||||
* a submenu — and renaming a chapter while recolouring it then meant two
|
||||
* requests for one decision. */
|
||||
const editChapter = useCallback(async (id: string, updates: { name?: string; color?: string | null }) => {
|
||||
setChapters((p) => p.map((c) => (c.id === id ? { ...c, ...updates } : c)));
|
||||
await sync.updateChapter(id, updates);
|
||||
}, []);
|
||||
|
||||
const deleteChapter = useCallback(
|
||||
@@ -434,8 +437,7 @@ export function useNoteData(user: User | null, activeNote: string | null): NoteD
|
||||
updateNotebook,
|
||||
chapters,
|
||||
createChapter,
|
||||
renameChapter,
|
||||
recolourChapter,
|
||||
editChapter,
|
||||
deleteChapter,
|
||||
deleteNotebook,
|
||||
reorderNotebooks,
|
||||
|
||||
@@ -537,7 +537,7 @@ type QueuedAction =
|
||||
};
|
||||
}
|
||||
| { action: "deleteNotebook"; data: { id: string } }
|
||||
| { action: "createChapter"; data: { localId: string; notebookId: string; name: string } }
|
||||
| { action: "createChapter"; data: { localId: string; notebookId: string; name: string; color?: string | null } }
|
||||
| { action: "updateChapter"; data: { id: string; name?: string; sortOrder?: number; color?: string | null } }
|
||||
| { action: "deleteChapter"; data: { id: string } }
|
||||
| {
|
||||
@@ -648,7 +648,11 @@ async function processEntry(entry: QueueEntry): Promise<void> {
|
||||
break;
|
||||
case "createChapter": {
|
||||
const d = a.data;
|
||||
const r = await api.post<{ id: string }>("/chapters", { notebookId: d.notebookId, name: d.name });
|
||||
const r = await api.post<{ id: string }>("/chapters", {
|
||||
notebookId: d.notebookId,
|
||||
name: d.name,
|
||||
color: d.color ?? null,
|
||||
});
|
||||
if (r.id !== d.localId) {
|
||||
const local = (await db.getAllChapters()).find((c) => c.id === d.localId);
|
||||
await db.deleteChapter(d.localId);
|
||||
@@ -656,6 +660,7 @@ async function processEntry(entry: QueueEntry): Promise<void> {
|
||||
id: r.id,
|
||||
notebookId: d.notebookId,
|
||||
name: d.name,
|
||||
color: d.color ?? null,
|
||||
sortOrder: local?.sortOrder ?? 0,
|
||||
});
|
||||
// Notes filed into it while it was local, and anything still queued
|
||||
@@ -816,12 +821,16 @@ export async function fetchChapters(): Promise<Chapter[]> {
|
||||
return [...chapters, ...pending];
|
||||
}
|
||||
|
||||
export async function createChapter(notebookId: string, name: string): Promise<Chapter> {
|
||||
export async function createChapter(
|
||||
notebookId: string,
|
||||
name: string,
|
||||
color: string | null = null
|
||||
): Promise<Chapter> {
|
||||
const sortOrder = (await db.getAllChapters()).filter((c) => c.notebookId === notebookId).length;
|
||||
if (_online && api.token) {
|
||||
try {
|
||||
const r = await api.post<{ id: string }>("/chapters", { notebookId, name });
|
||||
const chapter: Chapter = { id: r.id, notebookId, name, sortOrder };
|
||||
const r = await api.post<{ id: string }>("/chapters", { notebookId, name, color });
|
||||
const chapter: Chapter = { id: r.id, notebookId, name, color, sortOrder };
|
||||
await db.putChapter(chapter);
|
||||
notifyData("chapter", chapter);
|
||||
return chapter;
|
||||
@@ -830,9 +839,9 @@ export async function createChapter(notebookId: string, name: string): Promise<C
|
||||
}
|
||||
}
|
||||
const lid = "ch_" + Math.random().toString(36).slice(2, 10);
|
||||
const chapter: Chapter = { id: lid, notebookId, name, sortOrder, _pendingCreate: true };
|
||||
const chapter: Chapter = { id: lid, notebookId, name, color, sortOrder, _pendingCreate: true };
|
||||
await db.putChapter(chapter);
|
||||
await enqueue({ action: "createChapter", data: { localId: lid, notebookId, name } });
|
||||
await enqueue({ action: "createChapter", data: { localId: lid, notebookId, name, color } });
|
||||
notifyData("chapter", chapter);
|
||||
notifyStatus("pending");
|
||||
return chapter;
|
||||
|
||||
@@ -10,9 +10,24 @@ import { useEffect, useMemo, useRef, useState } from "react";
|
||||
*
|
||||
* Deliberately small. Naming a chapter is one field and two buttons, and
|
||||
* anything more would be a form.
|
||||
*
|
||||
* The one exception is the colour, offered when `colors` is given: it belongs to
|
||||
* the same act. Making a chapter and then having to right-click it to say which
|
||||
* one it is meant two steps for one decision.
|
||||
*/
|
||||
export function NameDialog({ title, value = "", label = "Name", confirmLabel = "Save", onSave, onClose, dark }) {
|
||||
export function NameDialog({
|
||||
title,
|
||||
value = "",
|
||||
label = "Name",
|
||||
confirmLabel = "Save",
|
||||
colors,
|
||||
color: initialColor = null,
|
||||
onSave,
|
||||
onClose,
|
||||
dark,
|
||||
}) {
|
||||
const [name, setName] = useState(value);
|
||||
const [color, setColor] = useState(initialColor);
|
||||
const inputRef = useRef(null);
|
||||
|
||||
const T = useMemo(
|
||||
@@ -35,7 +50,7 @@ export function NameDialog({ title, value = "", label = "Name", confirmLabel = "
|
||||
const save = () => {
|
||||
const trimmed = name.trim();
|
||||
if (!trimmed) return;
|
||||
onSave(trimmed);
|
||||
onSave(trimmed, color);
|
||||
onClose();
|
||||
};
|
||||
|
||||
@@ -89,6 +104,42 @@ export function NameDialog({ title, value = "", label = "Name", confirmLabel = "
|
||||
boxSizing: "border-box",
|
||||
}}
|
||||
/>
|
||||
{colors?.length > 0 && (
|
||||
<div style={{ marginTop: 16 }}>
|
||||
<div style={{ fontSize: 11.5, color: T.muted, fontWeight: 600, marginBottom: 8 }}>Colour</div>
|
||||
<div style={{ display: "flex", flexWrap: "wrap", gap: 8 }}>
|
||||
{[{ label: "None", value: null }, ...colors].map((c) => {
|
||||
const picked = (c.value ?? null) === (color ?? null);
|
||||
return (
|
||||
<button
|
||||
key={c.label}
|
||||
type="button"
|
||||
onClick={() => setColor(c.value ?? null)}
|
||||
title={c.label}
|
||||
aria-label={c.label}
|
||||
aria-pressed={picked}
|
||||
data-colour-option={c.value ?? "none"}
|
||||
style={{
|
||||
width: 28,
|
||||
height: 28,
|
||||
borderRadius: 9,
|
||||
cursor: "pointer",
|
||||
// The ring, not a tick: a tick over a dark swatch is
|
||||
// unreadable and over a light one it is the only thing seen.
|
||||
border: picked ? `2px solid ${T.text}` : `1px solid ${T.border}`,
|
||||
background: c.value ?? "transparent",
|
||||
color: T.muted,
|
||||
fontSize: 11,
|
||||
lineHeight: 1,
|
||||
}}
|
||||
>
|
||||
{c.value ? "" : "—"}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div style={{ display: "flex", justifyContent: "flex-end", gap: 8, marginTop: 18 }}>
|
||||
<button
|
||||
onClick={onClose}
|
||||
|
||||
@@ -440,7 +440,7 @@ docker compose -f docker-compose.dev.yml up -d --build # must be running
|
||||
| `conflict` | 10 | a late write wins but the state it buries survives as a version |
|
||||
| `export` | 21 | the downloaded bytes are real Markdown, ZIP and PNG, unpacked and read back |
|
||||
| `organise` | 53 | a hand-made order survives a search; separators hold their place; a note exported and imported comes back whole |
|
||||
| `chapters` | 31 | a filed note stays filed across a sync; the notebook's separators survive a chapter being opened; handles appear only while rearranging |
|
||||
| `chapters` | 34 | a filed note stays filed across a sync; the notebook's separators survive a chapter being opened; handles appear only while rearranging |
|
||||
| `offline` | 17 | a cold page load with the network off still opens notes and searches them — and a note made, written and drawn on offline arrives whole once there is a connection |
|
||||
| `sync` | 7 | a PDF written on one device — document, drawing and pinned notes — reaches a second one, which can find it by its contents without opening it |
|
||||
|
||||
@@ -996,11 +996,17 @@ Three rules that keep the two levels honest:
|
||||
the others back untouched.
|
||||
|
||||
A note made while a chapter is open is filed there without being moved. A chapter
|
||||
is named when it is created, unlike a separator: a separator is a line and reads
|
||||
perfectly well unnamed, while an unnamed chapter is a row nobody can tell from
|
||||
the next one. Each can be given **its own colour** — the point of having chapters
|
||||
is that they are different places, and eight named colours tell them apart where
|
||||
twenty near-identical blues would not.
|
||||
is **named and coloured when it is created**, unlike a separator: a separator is a
|
||||
line and reads perfectly well unnamed, while an unnamed chapter is a row nobody
|
||||
can tell from the next one. The colour is in the same dialog because it is the
|
||||
same decision — the point of having chapters is that they are different places,
|
||||
and eight named colours tell them apart where twenty near-identical blues would
|
||||
not. Both can be changed later from the chapter's own menu.
|
||||
|
||||
A chapter shows even when the notebook holds nothing else. "Nothing here" is
|
||||
asked of every row, not of the notes alone — asked of the notes, a notebook whose
|
||||
only contents were chapters showed the empty-list panel with the chapters hidden
|
||||
behind it, and making a chapter in a fresh notebook appeared to do nothing.
|
||||
|
||||
### How a chapter opens
|
||||
|
||||
@@ -1027,9 +1033,9 @@ own separators were filtered out and a drag in that list wrote nothing at all.
|
||||
**Which of the two you are in is a separate question again**, because both are on
|
||||
screen at once. Opening a chapter puts you in it; pressing in the notebook's own
|
||||
list puts you back in the notebook without closing the chapter. Whichever it is
|
||||
wears a line along its top and the word *here*, and the + button names the place
|
||||
it would make the note in. Before, a new note went into the chapter however far
|
||||
away you had since clicked, and nothing on screen said so.
|
||||
wears a line along its top, and the + button names the place it would make the
|
||||
note in. Before, a new note went into the chapter however far away you had since
|
||||
clicked, and nothing on screen said so.
|
||||
|
||||
### A chapter is a row like any other
|
||||
|
||||
|
||||
Reference in new issue
Block a user