chore(eslint): enable obsidianmd/prefer-active-doc (#2411)

Flip the rule from "off" to "error" to catch accidental references to
the global `document` (which always points at the main window even when
the user is interacting with a popout).

The only flagged site was a `document` field on the `RelevantNoteEntry`
type — not a DOM reference, but the rule pattern-matches the identifier.
Rename it to `note` so `entry.note.path/title` reads naturally next to
`entry.metadata.*`, and remove the suppression.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Zero Liu 2026-05-13 01:09:06 -07:00 committed by GitHub
parent 99b3b1e22a
commit 48794ce097
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 32 additions and 32 deletions

View file

@ -74,7 +74,7 @@ export default [
// obsidianmd: defer to follow-up PRs
"obsidianmd/vault/iterate": "error",
"obsidianmd/prefer-active-doc": "off",
"obsidianmd/prefer-active-doc": "error",
"obsidianmd/object-assign": "off",
"obsidianmd/ui/sentence-case": "off",
"obsidianmd/no-unsupported-api": "off",

View file

@ -112,7 +112,7 @@ function RelevantNote({
const loadContent = useCallback(async () => {
if (fileContent) return; // Don't load if we already have content
const file = app.vault.getAbstractFileByPath(note.document.path);
const file = app.vault.getAbstractFileByPath(note.note.path);
if (file instanceof TFile) {
const content = await app.vault.cachedRead(file);
@ -128,7 +128,7 @@ function RelevantNote({
// Take first 1000 characters as preview
setFileContent(cleanContent.slice(0, 1000) + (cleanContent.length > 1000 ? "..." : ""));
}
}, [fileContent, note.document.path]);
}, [fileContent, note.note.path]);
useEffect(() => {
if (isOpen) {
@ -161,7 +161,7 @@ function RelevantNote({
<a
draggable
onDragStart={(e) => {
const file = app.vault.getAbstractFileByPath(note.document.path);
const file = app.vault.getAbstractFileByPath(note.note.path);
if (file instanceof TFile) {
handleDragStart(e, file);
}
@ -179,9 +179,9 @@ function RelevantNote({
}
}}
className="tw-block tw-w-full tw-truncate tw-text-sm tw-font-bold tw-text-normal"
title={`${note.document.title} - drag to insert wikilink`}
title={`${note.note.title} - drag to insert wikilink`}
>
{note.document.title}
{note.note.title}
</a>
</div>
@ -198,7 +198,7 @@ function RelevantNote({
<CollapsibleContent>
<div className="tw-border-[0px] tw-border-t tw-border-solid tw-border-border tw-px-4 tw-py-2">
<div className="tw-whitespace-pre-wrap tw-text-wrap tw-break-all tw-text-xs tw-text-muted tw-opacity-75">
{note.document.path}
{note.note.path}
</div>
{fileContent && (
<div className="tw-overflow-hidden tw-whitespace-pre-wrap tw-border-t tw-border-border tw-pb-4 tw-pt-2 tw-text-xs tw-text-normal">
@ -243,11 +243,11 @@ function RelevantNotePopover({
children: React.ReactNode;
}) {
return (
<Popover key={note.document.path}>
<Popover key={note.note.path}>
<PopoverTrigger asChild>{children}</PopoverTrigger>
<PopoverContent className="tw-flex tw-w-fit tw-min-w-72 tw-max-w-96 tw-flex-col tw-gap-2 tw-overflow-hidden">
<span className="tw-text-sm tw-text-normal">{note.document.title}</span>
<span className="tw-text-xs tw-text-muted">{note.document.path}</span>
<span className="tw-text-sm tw-text-normal">{note.note.title}</span>
<span className="tw-text-xs tw-text-muted">{note.note.path}</span>
<div className="tw-flex tw-gap-2">
<button
onClick={onAddToChat}
@ -380,27 +380,27 @@ export const RelevantNotes = memo(
<div className="tw-flex tw-max-h-6 tw-flex-wrap tw-gap-x-2 tw-gap-y-1 tw-overflow-y-hidden tw-px-1">
{relevantNotes.map((note) => (
<RelevantNotePopover
key={note.document.path}
key={note.note.path}
note={note}
onAddToChat={() => addToChat(note.document.title)}
onAddToChat={() => addToChat(note.note.title)}
onNavigateToNote={(openInNewLeaf: boolean) =>
navigateToNote(note.document.path, openInNewLeaf)
navigateToNote(note.note.path, openInNewLeaf)
}
>
<Badge
variant="outline"
key={note.document.path}
key={note.note.path}
draggable
onDragStart={(e) => {
const file = app.vault.getAbstractFileByPath(note.document.path);
const file = app.vault.getAbstractFileByPath(note.note.path);
if (file instanceof TFile) {
handleDragStart(e, file);
}
}}
className="tw-max-w-40 tw-text-xs tw-text-muted hover:tw-cursor-pointer hover:tw-bg-interactive-hover"
title={`${note.document.title} - drag to insert wikilink`}
title={`${note.note.title} - drag to insert wikilink`}
>
<span className="tw-truncate">{note.document.title}</span>
<span className="tw-truncate">{note.note.title}</span>
</Badge>
</RelevantNotePopover>
))}
@ -411,10 +411,10 @@ export const RelevantNotes = memo(
{relevantNotes.map((note) => (
<RelevantNote
note={note}
key={note.document.path}
onAddToChat={() => addToChat(note.document.title)}
key={note.note.path}
onAddToChat={() => addToChat(note.note.title)}
onNavigateToNote={(openInNewLeaf: boolean) =>
navigateToNote(note.document.path, openInNewLeaf)
navigateToNote(note.note.path, openInNewLeaf)
}
/>
))}

View file

@ -177,14 +177,14 @@ describe("findRelevantNotes", () => {
const result = await findRelevantNotes({ filePath: "source.md" });
expect(result.map((entry) => entry.document.path)).toEqual([
expect(result.map((entry) => entry.note.path)).toEqual([
"second.md",
"first.md",
"linked-only.md",
]);
expect(
result.find((entry) => entry.document.path === "second.md")?.metadata.similarityScore
).toBe(0.82);
expect(result.find((entry) => entry.note.path === "second.md")?.metadata.similarityScore).toBe(
0.82
);
expect(mockGetDb).toHaveBeenCalledTimes(1);
expect(mockGetDocsByEmbedding).toHaveBeenCalledTimes(2);
expect(mockSearchRelated).not.toHaveBeenCalled();
@ -224,10 +224,10 @@ describe("findRelevantNotes", () => {
const result = await findRelevantNotes({ filePath: "source.md" });
expect(result.map((entry) => entry.document.path)).toEqual(["beta.md", "alpha.md"]);
expect(
result.find((entry) => entry.document.path === "alpha.md")?.metadata.similarityScore
).toBe(0.6);
expect(result.map((entry) => entry.note.path)).toEqual(["beta.md", "alpha.md"]);
expect(result.find((entry) => entry.note.path === "alpha.md")?.metadata.similarityScore).toBe(
0.6
);
expect(mockGetDb).not.toHaveBeenCalled();
expect(mockGetDocumentsByPath).not.toHaveBeenCalled();
expect(mockSearchRelated).toHaveBeenCalledTimes(1);
@ -259,7 +259,7 @@ describe("findRelevantNotes", () => {
const result = await findRelevantNotes({ filePath: "source.md" });
expect(result.map((e) => e.document.path)).toEqual(["alpha.md"]);
expect(result.map((e) => e.note.path)).toEqual(["alpha.md"]);
expect(result[0].metadata.similarityScore).toBe(0.75);
// Orama path not taken (no embeddings); Miyo called as fallback
expect(mockGetDocsByEmbedding).not.toHaveBeenCalled();
@ -289,7 +289,7 @@ describe("findRelevantNotes", () => {
const result = await findRelevantNotes({ filePath: "source.md" });
expect(result).toHaveLength(1);
expect(result[0].document.path).toBe("linked-only.md");
expect(result[0].note.path).toBe("linked-only.md");
expect(result[0].metadata.similarityScore).toBeUndefined();
expect(result[0].metadata.hasOutgoingLinks).toBe(true);
expect(mockGetDocumentsByPath).not.toHaveBeenCalled();

View file

@ -276,7 +276,7 @@ function mergeScoreMaps(
}
export type RelevantNoteEntry = {
document: {
note: {
path: string;
title: string;
};
@ -327,7 +327,7 @@ export async function findRelevantNotes({
return null;
}
return {
document: {
note: {
path,
title: file.basename,
},