fix(anchors): detection no longer working

This commit is contained in:
Elian Doran
2026-04-19 20:56:07 +03:00
parent d97818a594
commit b43841157e
4 changed files with 66 additions and 14 deletions

View File

@@ -14,6 +14,25 @@ import { useTriliumEvent } from "../react/hooks";
type LinkType = "reference-link" | "external-link" | "hyper-link";
function findAnchorIds(content: string): string[] {
const re = /<a\b([^>]*)>(<\/a>)?/g;
const ids: string[] = [];
let match;
while ((match = re.exec(content))) {
const attrs = match[1];
if (/\bhref\s*=/.test(attrs)) continue;
const idMatch = /\bid\s*=\s*"([^"]+)"/.exec(attrs) ?? /\bid\s*=\s*'([^']+)'/.exec(attrs);
if (!idMatch) continue;
const id = idMatch[1];
if (!ids.includes(id)) ids.push(id);
}
return ids;
}
export interface AddLinkOpts {
text: string;
hasSelection: boolean;
@@ -67,12 +86,25 @@ export default function AddLinkDialog() {
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) ?? [];
(async () => {
const note = await froca.getNote(noteId);
if (cancelled || !note) return;
let bkms = note.getLabels("internalBookmark").map((l) => l.value);
// Fall back to scanning the note content for anchors if no labels
// are present (e.g. notes that predate the bookmark-label feature).
if (bkms.length === 0 && note.type === "text") {
const content = await note.getContent();
if (cancelled) return;
if (typeof content === "string") {
bkms = findAnchorIds(content);
}
}
setBookmarks(bkms);
setSelectedBookmark("");
});
})();
}
resetExternalLink();
}

View File

@@ -279,8 +279,10 @@ export default class Becca {
*/
getFlatTextIndex(): { notes: BNote[], flatTexts: string[], noteIdToIdx: Map<string, number> } {
if (!this.flatTextIndex) {
// Measure heap before building
const heapBefore = process.memoryUsage().heapUsed;
// Measure heap before building (only available under Node.js)
const heapBefore = typeof process !== "undefined" && typeof process.memoryUsage === "function"
? process.memoryUsage().heapUsed
: null;
const allNoteSet = this.getAllNoteSet();
const notes: BNote[] = [];
@@ -296,10 +298,12 @@ export default class Becca {
this.flatTextIndex = { notes, flatTexts, noteIdToIdx };
this.dirtyFlatTextNoteIds.clear();
// Measure heap after building and log
const heapAfter = process.memoryUsage().heapUsed;
const heapDelta = heapAfter - heapBefore;
getLog().info(`Flat text search index built: ${notes.length} notes, ${formatSize(heapDelta)}`);
if (heapBefore !== null) {
const heapDelta = process.memoryUsage().heapUsed - heapBefore;
getLog().info(`Flat text search index built: ${notes.length} notes, ${formatSize(heapDelta)}`);
} else {
getLog().info(`Flat text search index built: ${notes.length} notes`);
}
} else if (this.dirtyFlatTextNoteIds.size > 0) {
// Incremental update: only recompute flat texts for dirtied notes
const { flatTexts, noteIdToIdx } = this.flatTextIndex;

View File

@@ -118,7 +118,15 @@ class BAttribute extends AbstractBeccaEntity<BAttribute> {
}
isAutoLink() {
return this.type === "relation" && ["internalLink", "imageLink", "relationMapLink", "includeNoteLink"].includes(this.name);
if (this.type === "relation") {
return ["internalLink", "imageLink", "relationMapLink", "includeNoteLink"].includes(this.name);
}
if (this.type === "label") {
return this.name === "internalBookmark";
}
return false;
}
get note() {

View File

@@ -515,19 +515,27 @@ function findIncludeNoteLinks(content: string, foundLinks: FoundLink[]) {
/**
* Extracts bookmark IDs from CKEditor bookmark anchors (`<a id="..."></a>` without href).
* Bookmarks are stored as labels on the note so they can be looked up without parsing content.
* Matches id regardless of attribute order; skips anchors with href (those are regular links).
*/
export function findBookmarks(content: string): string[] {
const re = /<a\s+id="([^"]+)"[^>]*>(<\/a>)?/g;
const re = /<a\b([^>]*)>(<\/a>)?/g;
const bookmarks: string[] = [];
let match;
while ((match = re.exec(content))) {
const attrs = match[1];
// Skip anchors that also have an href (those are regular links, not bookmarks)
if (match[0].includes("href=")) {
if (/\bhref\s*=/.test(attrs)) {
continue;
}
const id = match[1];
const idMatch = /\bid\s*=\s*"([^"]+)"/.exec(attrs) ?? /\bid\s*=\s*'([^']+)'/.exec(attrs);
if (!idMatch) {
continue;
}
const id = idMatch[1];
if (!bookmarks.includes(id)) {
bookmarks.push(id);
}