Enhance note adoption process with confidence evaluation and settings
- Introduced a new function to evaluate adoption confidence based on file creation time and content criteria. - Updated the adoption confirmation logic to determine when to show confirmation based on user settings. - Added new settings for adoption confirmation mode and high-confidence time window in the settings interface. - Improved the user experience by providing clearer options for managing note adoption behavior.
This commit is contained in:
parent
90eafb62f4
commit
a6ce268b04
35
src/main.ts
35
src/main.ts
|
|
@ -39,6 +39,7 @@ import {
|
|||
isAdoptCandidate,
|
||||
matchesPendingHint,
|
||||
mergeFrontmatter,
|
||||
evaluateAdoptionConfidence,
|
||||
type PendingCreateHint,
|
||||
} from "./unresolvedLink/adoptHelpers";
|
||||
import { AdoptNoteModal } from "./ui/AdoptNoteModal";
|
||||
|
|
@ -745,21 +746,31 @@ export default class MindnetCausalAssistantPlugin extends Plugin {
|
|||
return;
|
||||
}
|
||||
|
||||
// Check if matches pending hint (high confidence)
|
||||
const highConfidence = matchesPendingHint(file, this.pendingCreateHint);
|
||||
// Evaluate confidence level
|
||||
const confidence = evaluateAdoptionConfidence(
|
||||
file,
|
||||
content,
|
||||
this.settings.adoptMaxChars,
|
||||
this.pendingCreateHint,
|
||||
this.settings.highConfidenceWindowMs
|
||||
);
|
||||
|
||||
if (this.settings.debugLogging) {
|
||||
console.log(`[Mindnet] Adopt candidate detected: ${file.path}`, {
|
||||
highConfidence,
|
||||
confidence,
|
||||
pendingHint: this.pendingCreateHint,
|
||||
fileCtime: file.stat.ctime,
|
||||
timeSinceCreation: Date.now() - file.stat.ctime,
|
||||
});
|
||||
}
|
||||
|
||||
// Clear pending hint (used once)
|
||||
this.pendingCreateHint = null;
|
||||
|
||||
// Show confirm modal if not high confidence
|
||||
if (!highConfidence) {
|
||||
// Determine if we need to show confirmation
|
||||
const shouldShowConfirm = this.shouldShowAdoptionConfirm(confidence);
|
||||
|
||||
if (shouldShowConfirm) {
|
||||
const adoptModal = new AdoptNoteModal(this.app, file.basename);
|
||||
const result = await adoptModal.show();
|
||||
if (!result.adopt) {
|
||||
|
|
@ -797,6 +808,20 @@ export default class MindnetCausalAssistantPlugin extends Plugin {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if adoption confirmation should be shown based on mode and confidence.
|
||||
*/
|
||||
private shouldShowAdoptionConfirm(confidence: "high" | "low"): boolean {
|
||||
if (this.settings.adoptConfirmMode === "always") {
|
||||
return true;
|
||||
}
|
||||
if (this.settings.adoptConfirmMode === "never") {
|
||||
return false;
|
||||
}
|
||||
// onlyLowConfidence: show only for low confidence
|
||||
return confidence === "low";
|
||||
}
|
||||
|
||||
/**
|
||||
* Adopt a note: convert to Mindnet format and start wizard.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ export interface MindnetSettings {
|
|||
debugLogging: boolean; // Enable debug logging for unresolved link handling
|
||||
adoptNewNotesInEditor: boolean; // Auto-adopt newly created notes in editor
|
||||
adoptMaxChars: number; // Max content length to consider note as adopt-candidate
|
||||
adoptConfirmMode: "always" | "onlyLowConfidence" | "never"; // When to show adoption confirmation
|
||||
highConfidenceWindowMs: number; // Time window in ms for high-confidence adoption
|
||||
// Semantic mapping builder settings
|
||||
mappingWrapperCalloutType: string; // default: "abstract"
|
||||
mappingWrapperTitle: string; // default: "🕸️ Semantic Mapping"
|
||||
|
|
@ -44,6 +46,8 @@ export interface MindnetSettings {
|
|||
debugLogging: false,
|
||||
adoptNewNotesInEditor: true,
|
||||
adoptMaxChars: 200,
|
||||
adoptConfirmMode: "onlyLowConfidence",
|
||||
highConfidenceWindowMs: 3000,
|
||||
mappingWrapperCalloutType: "abstract",
|
||||
mappingWrapperTitle: "🕸️ Semantic Mapping",
|
||||
mappingWrapperFolded: true,
|
||||
|
|
|
|||
145
src/tests/unresolvedLink/adoptConfidence.test.ts
Normal file
145
src/tests/unresolvedLink/adoptConfidence.test.ts
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
import { describe, it, expect } from "vitest";
|
||||
import {
|
||||
evaluateAdoptionConfidence,
|
||||
type PendingCreateHint,
|
||||
} from "../../unresolvedLink/adoptHelpers";
|
||||
import { TFile } from "obsidian";
|
||||
|
||||
describe("evaluateAdoptionConfidence", () => {
|
||||
it("returns high confidence for recent file with matching hint", () => {
|
||||
const file = {
|
||||
basename: "test-note",
|
||||
stat: { ctime: Date.now() - 1000 }, // 1 second ago
|
||||
} as TFile;
|
||||
const content = "Some content";
|
||||
const hint: PendingCreateHint = {
|
||||
basename: "test-note",
|
||||
sourcePath: "source.md",
|
||||
timestamp: Date.now() - 500,
|
||||
};
|
||||
|
||||
const result = evaluateAdoptionConfidence(file, content, 200, hint, 3000);
|
||||
expect(result).toBe("high");
|
||||
});
|
||||
|
||||
it("returns low confidence for old file", () => {
|
||||
const file = {
|
||||
basename: "test-note",
|
||||
stat: { ctime: Date.now() - 10000 }, // 10 seconds ago
|
||||
} as TFile;
|
||||
const content = "Some content";
|
||||
const hint: PendingCreateHint = {
|
||||
basename: "test-note",
|
||||
sourcePath: "source.md",
|
||||
timestamp: Date.now() - 500,
|
||||
};
|
||||
|
||||
const result = evaluateAdoptionConfidence(file, content, 200, hint, 3000);
|
||||
expect(result).toBe("low");
|
||||
});
|
||||
|
||||
it("returns low confidence for file with id", () => {
|
||||
const file = {
|
||||
basename: "test-note",
|
||||
stat: { ctime: Date.now() - 1000 },
|
||||
} as TFile;
|
||||
const content = `---
|
||||
id: note_123
|
||||
---
|
||||
Body`;
|
||||
const hint: PendingCreateHint = {
|
||||
basename: "test-note",
|
||||
sourcePath: "source.md",
|
||||
timestamp: Date.now() - 500,
|
||||
};
|
||||
|
||||
const result = evaluateAdoptionConfidence(file, content, 200, hint, 3000);
|
||||
expect(result).toBe("low");
|
||||
});
|
||||
|
||||
it("returns low confidence for file exceeding maxChars", () => {
|
||||
const file = {
|
||||
basename: "test-note",
|
||||
stat: { ctime: Date.now() - 1000 },
|
||||
} as TFile;
|
||||
const content = "x".repeat(300); // Exceeds maxChars
|
||||
const hint: PendingCreateHint = {
|
||||
basename: "test-note",
|
||||
sourcePath: "source.md",
|
||||
timestamp: Date.now() - 500,
|
||||
};
|
||||
|
||||
const result = evaluateAdoptionConfidence(file, content, 200, hint, 3000);
|
||||
expect(result).toBe("low");
|
||||
});
|
||||
|
||||
it("returns high confidence for very recent file even without hint", () => {
|
||||
const file = {
|
||||
basename: "test-note",
|
||||
stat: { ctime: Date.now() - 500 }, // Very recent (0.5 seconds)
|
||||
} as TFile;
|
||||
const content = "Some content";
|
||||
|
||||
const result = evaluateAdoptionConfidence(file, content, 200, null, 3000);
|
||||
expect(result).toBe("high");
|
||||
});
|
||||
|
||||
it("returns low confidence for recent file without hint if not very recent", () => {
|
||||
const file = {
|
||||
basename: "test-note",
|
||||
stat: { ctime: Date.now() - 2500 }, // 2.5 seconds ago (not very recent)
|
||||
} as TFile;
|
||||
const content = "Some content";
|
||||
|
||||
const result = evaluateAdoptionConfidence(file, content, 200, null, 3000);
|
||||
expect(result).toBe("low");
|
||||
});
|
||||
|
||||
it("returns high confidence when hint matches even if file is slightly older", () => {
|
||||
const file = {
|
||||
basename: "test-note",
|
||||
stat: { ctime: Date.now() - 2500 }, // 2.5 seconds ago
|
||||
} as TFile;
|
||||
const content = "Some content";
|
||||
const hint: PendingCreateHint = {
|
||||
basename: "test-note",
|
||||
sourcePath: "source.md",
|
||||
timestamp: Date.now() - 2000, // Hint is 2 seconds ago, within window
|
||||
};
|
||||
|
||||
const result = evaluateAdoptionConfidence(file, content, 200, hint, 3000);
|
||||
expect(result).toBe("high");
|
||||
});
|
||||
});
|
||||
|
||||
describe("adoptConfirmMode behavior", () => {
|
||||
// Pure function to test confirm mode logic
|
||||
function shouldShowAdoptionConfirm(
|
||||
mode: "always" | "onlyLowConfidence" | "never",
|
||||
confidence: "high" | "low"
|
||||
): boolean {
|
||||
if (mode === "always") {
|
||||
return true;
|
||||
}
|
||||
if (mode === "never") {
|
||||
return false;
|
||||
}
|
||||
// onlyLowConfidence: show only for low confidence
|
||||
return confidence === "low";
|
||||
}
|
||||
|
||||
it("always mode shows confirm for both high and low confidence", () => {
|
||||
expect(shouldShowAdoptionConfirm("always", "high")).toBe(true);
|
||||
expect(shouldShowAdoptionConfirm("always", "low")).toBe(true);
|
||||
});
|
||||
|
||||
it("never mode never shows confirm", () => {
|
||||
expect(shouldShowAdoptionConfirm("never", "high")).toBe(false);
|
||||
expect(shouldShowAdoptionConfirm("never", "low")).toBe(false);
|
||||
});
|
||||
|
||||
it("onlyLowConfidence mode shows confirm only for low confidence", () => {
|
||||
expect(shouldShowAdoptionConfirm("onlyLowConfidence", "high")).toBe(false);
|
||||
expect(shouldShowAdoptionConfirm("onlyLowConfidence", "low")).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
@ -347,6 +347,41 @@ export class MindnetSettingTab extends PluginSettingTab {
|
|||
})
|
||||
);
|
||||
|
||||
// Adopt confirm mode
|
||||
new Setting(containerEl)
|
||||
.setName("Adopt confirm mode")
|
||||
.setDesc("When to show adoption confirmation: always, onlyLowConfidence (skip for high-confidence), or never")
|
||||
.addDropdown((dropdown) =>
|
||||
dropdown
|
||||
.addOption("always", "Always ask")
|
||||
.addOption("onlyLowConfidence", "Only for low confidence")
|
||||
.addOption("never", "Never ask")
|
||||
.setValue(this.plugin.settings.adoptConfirmMode)
|
||||
.onChange(async (value) => {
|
||||
if (value === "always" || value === "onlyLowConfidence" || value === "never") {
|
||||
this.plugin.settings.adoptConfirmMode = value;
|
||||
await this.plugin.saveSettings();
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// High confidence window
|
||||
new Setting(containerEl)
|
||||
.setName("High confidence window (ms)")
|
||||
.setDesc("Time window in milliseconds for high-confidence adoption (default: 3000)")
|
||||
.addText((text) =>
|
||||
text
|
||||
.setPlaceholder("3000")
|
||||
.setValue(String(this.plugin.settings.highConfidenceWindowMs))
|
||||
.onChange(async (value) => {
|
||||
const numValue = parseInt(value, 10);
|
||||
if (!isNaN(numValue) && numValue > 0) {
|
||||
this.plugin.settings.highConfidenceWindowMs = numValue;
|
||||
await this.plugin.saveSettings();
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// Semantic Mapping Builder section
|
||||
containerEl.createEl("h2", { text: "Semantic Mapping Builder" });
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ export function isAdoptCandidate(
|
|||
|
||||
/**
|
||||
* Check if a created file matches a pending create hint.
|
||||
* Returns true if basename matches and within time window (3 seconds).
|
||||
* Returns true if basename matches and within time window.
|
||||
*/
|
||||
export function matchesPendingHint(
|
||||
file: TFile,
|
||||
|
|
@ -57,6 +57,46 @@ export function matchesPendingHint(
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate confidence level for adoption.
|
||||
* Returns "high" if file is recently created and matches criteria, "low" otherwise.
|
||||
*/
|
||||
export function evaluateAdoptionConfidence(
|
||||
file: TFile,
|
||||
content: string,
|
||||
maxChars: number,
|
||||
hint: PendingCreateHint | null,
|
||||
timeWindowMs: number
|
||||
): "high" | "low" {
|
||||
// Check if file is recently created (within time window)
|
||||
const fileCtime = file.stat.ctime;
|
||||
const timeSinceCreation = Date.now() - fileCtime;
|
||||
const isRecent = timeSinceCreation >= 0 && timeSinceCreation <= timeWindowMs;
|
||||
|
||||
if (!isRecent) {
|
||||
return "low";
|
||||
}
|
||||
|
||||
// Check if adopt candidate (no id, content length OK)
|
||||
if (!isAdoptCandidate(content, maxChars)) {
|
||||
return "low";
|
||||
}
|
||||
|
||||
// High confidence if pending hint matches (optional but recommended)
|
||||
if (matchesPendingHint(file, hint, timeWindowMs)) {
|
||||
return "high";
|
||||
}
|
||||
|
||||
// Also high confidence if file is very recent (within window) and adopt candidate
|
||||
// This handles cases where pending hint might not be set
|
||||
if (isRecent && timeSinceCreation <= 2000) {
|
||||
// Very recent (within 2 seconds) = high confidence
|
||||
return "high";
|
||||
}
|
||||
|
||||
return "low";
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge new frontmatter into existing content.
|
||||
* Preserves existing frontmatter fields and body content.
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user