Temp build for backlinks
This commit is contained in:
parent
d1d436b020
commit
b3787eeb3c
4 changed files with 123 additions and 0 deletions
74
frontend/src/extensions/NoteLink.ts
Normal file
74
frontend/src/extensions/NoteLink.ts
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
import { Mark, markInputRule } from "@tiptap/core";
|
||||
|
||||
export const NoteLink = Mark.create({
|
||||
name: "noteLink",
|
||||
inclusive: false,
|
||||
addAttributes() {
|
||||
return {
|
||||
noteId: {
|
||||
default: null,
|
||||
parseHTML: (element) => {
|
||||
return element.getAttribute("data-note-id");
|
||||
},
|
||||
renderHTML: (attributes) => {
|
||||
return {
|
||||
"data-note-id": attributes.noteId,
|
||||
};
|
||||
},
|
||||
},
|
||||
title: {
|
||||
default: "",
|
||||
parseHTML: (element) => {
|
||||
return element.getAttribute("data-title");
|
||||
},
|
||||
renderHTML: (attributes) => {
|
||||
return {
|
||||
"data-title": attributes.title,
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
parseHTML() {
|
||||
return [
|
||||
{
|
||||
tag: "span[data-note-id]",
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
renderHTML({ HTMLAttributes }) {
|
||||
return ["span", { ...HTMLAttributes, class: "note-link" }, 0];
|
||||
},
|
||||
|
||||
addInputRules() {
|
||||
return [
|
||||
markInputRule({
|
||||
find: /\[\[([^\]]+)\]\]$/,
|
||||
type: this.type,
|
||||
getAttributes: (match) => {
|
||||
const title = match[1];
|
||||
// TODO: Look up noteId from title
|
||||
return {
|
||||
title,
|
||||
noteId: null, // For now
|
||||
};
|
||||
},
|
||||
}),
|
||||
];
|
||||
},
|
||||
|
||||
addStorage() {
|
||||
return {
|
||||
markdown: {
|
||||
serialize: (state, mark, parent, index) => {
|
||||
// Get the text content
|
||||
const textContent = parent.child(index).text || mark.attrs.title;
|
||||
|
||||
// Return the complete link, no wrapping
|
||||
return `[[${mark.attrs.title}:${mark.attrs.noteId}]]`;
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
@ -22,6 +22,7 @@ import SquareCheckIcon from "../assets/fontawesome/svg/square-check.svg?react";
|
|||
import CodeBracketIcon from "../assets/fontawesome/svg/code-simple.svg?react";
|
||||
// @ts-ignore
|
||||
import QuoteLeftIcon from "../assets/fontawesome/svg/quote-left.svg?react";
|
||||
import { NoteLink } from "@/extensions/NoteLink";
|
||||
|
||||
interface TiptapEditorProps {
|
||||
content: string;
|
||||
|
|
@ -37,6 +38,7 @@ export const TiptapEditor = ({
|
|||
const editor = useEditor({
|
||||
extensions: [
|
||||
ListKit,
|
||||
NoteLink,
|
||||
StarterKit.configure({
|
||||
heading: {
|
||||
levels: [1, 2, 3, 4, 5, 6],
|
||||
|
|
@ -189,6 +191,19 @@ export const TiptapEditor = ({
|
|||
</div>*/}
|
||||
|
||||
{/* Editor content */}
|
||||
<button
|
||||
onClick={() => {
|
||||
editor
|
||||
?.chain()
|
||||
.focus()
|
||||
.setMark("noteLink", { noteId: 123, title: "Test Note" })
|
||||
.insertContent("Test Note")
|
||||
.run();
|
||||
}}
|
||||
className="bg-ctp-blue text-ctp-base px-4 py-2 rounded"
|
||||
>
|
||||
Insert Test Link
|
||||
</button>
|
||||
<EditorContent
|
||||
editor={editor}
|
||||
className="editor-content h-min-screen p-4! pt-0!"
|
||||
|
|
|
|||
|
|
@ -172,3 +172,9 @@
|
|||
margin-top: 0 !important;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.note-link {
|
||||
@apply text-ctp-pink-500;
|
||||
@apply underline;
|
||||
@apply cursor-pointer;
|
||||
}
|
||||
|
|
|
|||
28
frontend/src/utils/notes.ts
Normal file
28
frontend/src/utils/notes.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import { Folder, FolderTreeNode, FolderTreeResponse } from "@/api/folders";
|
||||
import { NoteRead } from "@/api/notes";
|
||||
|
||||
export const createNoteMap = (
|
||||
folderTree: FolderTreeResponse,
|
||||
): Map<number, NoteRead> => {
|
||||
const flatenedNotes = flattenNotes(folderTree);
|
||||
const noteMap = new Map();
|
||||
|
||||
for (const note of flatenedNotes) {
|
||||
noteMap.set(note.id, note);
|
||||
}
|
||||
|
||||
return noteMap;
|
||||
};
|
||||
|
||||
export const flattenNotes = (response: FolderTreeResponse): NoteRead[] => {
|
||||
const allNotes = [...response.orphanedNotes];
|
||||
|
||||
const processFolder = (folder: FolderTreeNode) => {
|
||||
allNotes.push(...folder.notes);
|
||||
folder.children.forEach(processFolder);
|
||||
};
|
||||
|
||||
response.folders.forEach(processFolder);
|
||||
|
||||
return allNotes;
|
||||
};
|
||||
Loading…
Reference in a new issue