fix(note_map): freezing the app if there are too many notes (closes #8916)

This commit is contained in:
Elian Doran
2026-04-10 20:13:00 +03:00
parent ee229bd0d7
commit 540b607459
3 changed files with 23 additions and 3 deletions

View File

@@ -860,7 +860,8 @@
"collapse": "Collapse to normal size",
"title": "Note Map",
"fix-nodes": "Fix nodes",
"link-distance": "Link distance"
"link-distance": "Link distance",
"too-many-notes": "This subtree contains {{count}} notes, which exceeds the limit of {{max}} that can be displayed in the note map."
},
"note_paths": {
"title": "Note Paths",

View File

@@ -1,5 +1,5 @@
.note-detail-note-map {
height: 100%;
height: 100%;
overflow: hidden;
}
@@ -54,4 +54,4 @@
width: 10px;
}
/* End of styling the slider */
/* End of styling the slider */

View File

@@ -12,11 +12,15 @@ import { t } from "../../services/i18n";
import { getEffectiveThemeStyle } from "../../services/theme";
import ActionButton from "../react/ActionButton";
import { useElementSize, useNoteLabel } from "../react/hooks";
import NoItems from "../react/NoItems";
import Slider from "../react/Slider";
import { loadNotesAndRelations, NoteMapLinkObject, NoteMapNodeObject, NotesAndRelationsData } from "./data";
import { CssData, setupRendering } from "./rendering";
import { MapType, NoteMapWidgetMode, rgb2hex } from "./utils";
/** Maximum number of notes to render in the note map before showing a warning. */
const MAX_NOTES_THRESHOLD = 1_000;
interface NoteMapProps {
note: FNote;
widgetMode: NoteMapWidgetMode;
@@ -34,6 +38,7 @@ export default function NoteMap({ note, widgetMode, parentRef }: NoteMapProps) {
const containerSize = useElementSize(parentRef);
const [ fixNodes, setFixNodes ] = useState(false);
const [ linkDistance, setLinkDistance ] = useState(40);
const [ tooManyNotes, setTooManyNotes ] = useState<number | null>(null);
const notesAndRelationsRef = useRef<NotesAndRelationsData>();
const mapRootId = useMemo(() => {
@@ -61,6 +66,14 @@ export default function NoteMap({ note, widgetMode, parentRef }: NoteMapProps) {
const includeRelations = labelValues("mapIncludeRelation");
loadNotesAndRelations(mapRootId, excludeRelations, includeRelations, mapType).then((notesAndRelations) => {
if (!containerRef.current || !styleResolverRef.current) return;
// Guard against rendering too many notes which would freeze the browser.
if (notesAndRelations.nodes.length > MAX_NOTES_THRESHOLD) {
setTooManyNotes(notesAndRelations.nodes.length);
return;
}
setTooManyNotes(null);
const cssData = getCssData(containerRef.current, styleResolverRef.current);
// Configure rendering properties.
@@ -119,6 +132,12 @@ export default function NoteMap({ note, widgetMode, parentRef }: NoteMapProps) {
});
}, [ fixNodes, mapType ]);
if (tooManyNotes) {
return (
<NoItems icon="bx bx-error-circle" text={t("note_map.too-many-notes", { count: tooManyNotes, max: MAX_NOTES_THRESHOLD })} />
);
}
return (
<div className="note-map-widget">
<div className="btn-group btn-group-sm map-type-switcher content-floating-buttons top-left" role="group">