diff --git a/apps/client/src/widgets/dialogs/add_link.tsx b/apps/client/src/widgets/dialogs/add_link.tsx
index c63aaed536..2206bd9dd1 100644
--- a/apps/client/src/widgets/dialogs/add_link.tsx
+++ b/apps/client/src/widgets/dialogs/add_link.tsx
@@ -50,13 +50,10 @@ export default function AddLinkDialog() {
setLinkTitle(title);
}
- function resetExternalLink() {
- if (linkType === "external-link") {
- setLinkType("reference-link");
- }
- }
-
useEffect(() => {
+ const resetExternalLink = () =>
+ setLinkType((prev) => prev === "external-link" ? "reference-link" : prev);
+
if (!suggestion) {
resetExternalLink();
setBookmarks([]);
@@ -64,11 +61,14 @@ export default function AddLinkDialog() {
return;
}
+ let cancelled = false;
+
if (suggestion.notePath) {
const noteId = tree.getNoteIdFromUrl(suggestion.notePath);
if (noteId) {
setDefaultLinkTitle(noteId);
froca.getNote(noteId).then((note) => {
+ if (cancelled) return;
const bkms = note?.getLabels("internalBookmark").map((l) => l.value) ?? [];
setBookmarks(bkms);
setSelectedBookmark("");
@@ -81,6 +81,8 @@ export default function AddLinkDialog() {
setLinkTitle(suggestion.externalLink);
setLinkType("external-link");
}
+
+ return () => { cancelled = true; };
}, [suggestion]);
useEffect(() => {
diff --git a/apps/server/src/services/notes.ts b/apps/server/src/services/notes.ts
index 56499f0288..13a0955774 100644
--- a/apps/server/src/services/notes.ts
+++ b/apps/server/src/services/notes.ts
@@ -480,7 +480,7 @@ export function findBookmarks(content: string): string[] {
function saveBookmarks(note: BNote, content: string) {
const foundBookmarks = findBookmarks(content);
- const existingBookmarks = note.getLabels("internalBookmark");
+ const existingBookmarks = note.getOwnedLabels("internalBookmark");
for (const bookmarkId of foundBookmarks) {
const existing = existingBookmarks.find((l) => l.value === bookmarkId);
diff --git a/packages/ckeditor5/src/plugins/copy_anchor_link.ts b/packages/ckeditor5/src/plugins/copy_anchor_link.ts
index 60dc8fa8b6..670b97b8f5 100644
--- a/packages/ckeditor5/src/plugins/copy_anchor_link.ts
+++ b/packages/ckeditor5/src/plugins/copy_anchor_link.ts
@@ -1,5 +1,6 @@
-import { ButtonView, Plugin, isWidget } from "ckeditor5";
+import { ButtonView, Plugin } from "ckeditor5";
import copyIcon from "../icons/copy.svg?raw";
+import { escapeHtml } from "../utils";
/**
* Adds a "Copy anchor link" button to the bookmark/anchor widget toolbar.
@@ -32,7 +33,7 @@ export default class CopyAnchorLinkButton extends Plugin {
if (noteId && bookmarkId) {
const href = `#root/${noteId}?bookmark=${encodeURIComponent(bookmarkId)}`;
const title = glob.getReferenceLinkTitleSync(href);
- const html = `${title}`;
+ const html = `${escapeHtml(title)}`;
navigator.clipboard.write([
new ClipboardItem({
"text/html": new Blob([html], { type: "text/html" }),
diff --git a/packages/ckeditor5/src/utils.ts b/packages/ckeditor5/src/utils.ts
index 4ed648d114..056538412d 100644
--- a/packages/ckeditor5/src/utils.ts
+++ b/packages/ckeditor5/src/utils.ts
@@ -1,5 +1,9 @@
import type { DifferItemAttribute, Editor, ModelDocumentFragment, ModelElement, ModelNode } from "ckeditor5";
+export function escapeHtml(str: string): string {
+ return str.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """);
+}
+
function hasHeadingAncestor(node: ModelElement | ModelNode | ModelDocumentFragment | null): boolean {
let current: ModelElement | ModelNode | ModelDocumentFragment | null = node;
while (current) {
diff --git a/packages/commons/src/lib/markdown_renderer.ts b/packages/commons/src/lib/markdown_renderer.ts
index d6d3f38a99..9b89d2cf5b 100644
--- a/packages/commons/src/lib/markdown_renderer.ts
+++ b/packages/commons/src/lib/markdown_renderer.ts
@@ -106,7 +106,7 @@ function handleH1(content: string, title: string): string {
});
}
-function extractCodeBlocks(text: string): { processedText: string; placeholderMap: Map } {
+export function extractCodeBlocks(text: string): { processedText: string; placeholderMap: Map } {
const codeMap = new Map();
let id = 0;
const timestamp = Date.now();