Five of the nine things on the list, and one finding that came out of them. Colour: a marker pen and an ink colour, written as marks here rather than installed — the schema of a note decides what can be stored in it forever. A highlight is a <mark> so a reader that drops the colour still shows the word as marked, and marked text is painted dark whatever the theme. Both travel as HTML in Markdown, like underline, and both are read back. The PDF fills the screen: the browser's own full screen for the window frame, an overlay for the sidebar and the list, Escape and a visible button for the way out. The dashboard leads with the recent notes, sorted by time whatever order the notebooks are in. A tap on the blank part of a page puts the caret at the nearest position instead of nowhere. A drag from the left edge shows the sidebar. The page has an edge on a phone, where it had none. The finding: at the speed a robot types, a long note loses keystrokes between the editor and the pagination measuring after each one — 87 of 220 paragraphs. Nothing is lost at 50 characters a second, so the page-count check now pastes its bulk text rather than measuring typing throughput. And the history suite asked for "the first note in the list" without waiting for the note to reach the server; it asks by name now. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
66 lines
2.6 KiB
JavaScript
66 lines
2.6 KiB
JavaScript
/** The one place that decides which nodes and marks a note may contain. */
|
|
|
|
import StarterKit from "@tiptap/starter-kit";
|
|
import Underline from "@tiptap/extension-underline";
|
|
import Link from "@tiptap/extension-link";
|
|
import Placeholder from "@tiptap/extension-placeholder";
|
|
import TaskList from "@tiptap/extension-task-list";
|
|
import TaskItem from "@tiptap/extension-task-item";
|
|
import Table from "@tiptap/extension-table";
|
|
import TableRow from "@tiptap/extension-table-row";
|
|
import TableHeader from "@tiptap/extension-table-header";
|
|
import TableCell from "@tiptap/extension-table-cell";
|
|
|
|
import { NoteLink } from "./noteLink.js";
|
|
import { TextAlign } from "./textAlign.js";
|
|
import { Highlight, TextColor } from "./textMarks.js";
|
|
import { Pagination } from "./pagination.js";
|
|
import { AttachmentImage } from "./attachmentImage.js";
|
|
|
|
export function buildExtensions({
|
|
search = () => [],
|
|
placeholder = "Start writing...",
|
|
// Where the page ends, read fresh on every measurement so a resize is enough
|
|
// to re-break the text. Absent when there is no page to break against — the
|
|
// static list used for parsing, for one.
|
|
getGeometry = null,
|
|
} = {}) {
|
|
return [
|
|
StarterKit.configure({
|
|
heading: { levels: [1, 2, 3] },
|
|
// History is on by default; the drawing layer keeps its own undo stack.
|
|
codeBlock: { HTMLAttributes: { class: "note-code-block" } },
|
|
}),
|
|
Underline,
|
|
Link.configure({
|
|
openOnClick: false, // the app decides what a click does
|
|
autolink: true,
|
|
protocols: ["http", "https", "mailto"],
|
|
HTMLAttributes: { rel: "noopener noreferrer nofollow", target: "_blank" },
|
|
}),
|
|
// allowBase64 stays on so notes written before attachments still render.
|
|
AttachmentImage.configure({ inline: false, allowBase64: true }),
|
|
Placeholder.configure({ placeholder }),
|
|
TaskList,
|
|
TaskItem.configure({ nested: true }),
|
|
Table.configure({ resizable: true, lastColumnResizable: false, allowTableNodeSelection: true }),
|
|
TableRow,
|
|
TableHeader,
|
|
TableCell,
|
|
NoteLink.configure({ search }),
|
|
TextAlign.configure({ types: ["paragraph", "heading"] }),
|
|
// A marker pen and a colour for the letters. Marks rather than a node, so
|
|
// they nest with bold and italic like every other emphasis.
|
|
Highlight,
|
|
TextColor,
|
|
Pagination.configure({ getGeometry }),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* A static extension list for parsing stored HTML outside of a live editor
|
|
* (`generateJSON`). It must accept everything `buildExtensions` produces, or
|
|
* migrating an old note would silently drop nodes.
|
|
*/
|
|
export const parsingExtensions = buildExtensions();
|