Let a margin remark become a note, and lead to it ever after
Some remarks turn into a page of their own. "Notiz anlegen" on a margin card makes one, titles it from what was written, and goes there; the pin keeps its id so the passage leads to that note afterwards. The note is created with a link back to the document, so the connection reads both ways and the document lists it among its backlinks — a note born from a passage should not be a note with nothing on it saying which passage. An existing note can be linked instead, and a link can be let go of without touching the note. Both are offered on the phone sheet too. Two older bugs stood in the way and are fixed: `openLinkTarget` finds a note's notebook by looking it up in the notes list, and a note created in the same tick is not in the copy that callback closed over — so jumping to a fresh note went nowhere at all. Creating one now navigates by the notebook it already knows. A drawing save waits 700ms for the next stroke, and leaving the document inside that window dropped it. That is precisely what turning a remark into a note does, so the link would have been lost on the way out. The viewer flushes a pending write when it unmounts, which also covers drawing and navigating away quickly. 215 unit tests, 229 browser checks across eleven suites. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
011ba82a94
commit
502aecfc92
10 files changed
+541
-12
No files matched your search
+64
-2
@@ -34,6 +34,7 @@ import { EditorPane } from "./features/notes/EditorPane.jsx";
|
||||
import { HistoryModal } from "./features/notes/HistoryModal.jsx";
|
||||
import { DrawingLayer } from "./features/drawing/DrawingLayer.jsx";
|
||||
import { CANON_W, CANON_H, PREVIEW_DRAWING_W } from "./features/drawing/strokes.js";
|
||||
import { serializeNoteContent } from "./editor/content";
|
||||
|
||||
/** Views that are not a notebook and therefore cannot hold a new note. */
|
||||
const VIRTUAL_FOLDERS = ["all", "starred", "trash", "dashboard"];
|
||||
@@ -279,9 +280,70 @@ export default function App() {
|
||||
[notes, openFolder, openNote]
|
||||
);
|
||||
|
||||
/**
|
||||
* Turns a remark in a PDF's margin into a note of its own.
|
||||
*
|
||||
* The new note opens with a link back to the document it came from, so the
|
||||
* connection reads in both directions: the pin leads here, and this note shows
|
||||
* up in the document's backlinks. Without that a note born from a passage
|
||||
* would have nothing on it saying which passage.
|
||||
*/
|
||||
const createNoteForPin = useCallback(
|
||||
async (source, title) => {
|
||||
const folderId = source.folderId || notebooks[0]?.id;
|
||||
if (!folderId) return null;
|
||||
const note = await data.createNote(folderId);
|
||||
const doc = {
|
||||
type: "doc",
|
||||
content: [
|
||||
{
|
||||
type: "paragraph",
|
||||
content: [
|
||||
{ type: "text", text: "Zu " },
|
||||
{
|
||||
type: "noteLink",
|
||||
attrs: { noteId: source.id, label: source.title || "Dokument", kind: "note" },
|
||||
},
|
||||
],
|
||||
},
|
||||
{ type: "paragraph" },
|
||||
],
|
||||
};
|
||||
data.updateNote(note.id, {
|
||||
title,
|
||||
content: serializeNoteContent(doc),
|
||||
links: [source.id],
|
||||
attachments: [],
|
||||
});
|
||||
return { ...note, title };
|
||||
},
|
||||
[data, notebooks]
|
||||
);
|
||||
|
||||
/**
|
||||
* Opens a note that was created a moment ago.
|
||||
*
|
||||
* `openLinkTarget` looks the target up in the notes list to find its notebook,
|
||||
* and a note created in the same tick is not in the copy that callback closed
|
||||
* over — so it went nowhere at all. Here the notebook is already known.
|
||||
*/
|
||||
const openCreatedNote = useCallback(
|
||||
(id, folderId) => {
|
||||
if (folderId) setActiveFolder(folderId);
|
||||
openNote(id);
|
||||
},
|
||||
[openNote]
|
||||
);
|
||||
|
||||
const linkProps = useMemo(
|
||||
() => ({ openLinkTarget, searchLinkTargets, resolveLinkLabel }),
|
||||
[openLinkTarget, searchLinkTargets, resolveLinkLabel]
|
||||
() => ({
|
||||
openLinkTarget,
|
||||
searchLinkTargets,
|
||||
resolveLinkLabel,
|
||||
createNoteForPin,
|
||||
openCreatedNote,
|
||||
}),
|
||||
[openLinkTarget, searchLinkTargets, resolveLinkLabel, createNoteForPin, openCreatedNote]
|
||||
);
|
||||
|
||||
/* ── Actions ── */
|
||||
|
||||
Reference in new issue
Block a user