Add graph schema loading and validation functionality
- Introduced methods for loading and reloading graph schema, including debounced reload on file changes. - Enhanced the semantic mapping builder to utilize the new graph schema loading mechanism for improved caching and live updates. - Added a validation button in the settings interface to check the existence and validity of the graph schema file, providing user feedback on the schema status. - Improved error handling for graph schema loading to ensure clear notifications for users in case of issues.
This commit is contained in:
parent
58b6ffffed
commit
1f9211cbc5
77
src/main.ts
77
src/main.ts
|
|
@ -20,6 +20,8 @@ import { InterviewWizardModal, type WizardResult } from "./ui/InterviewWizardMod
|
|||
import { extractTargetFromAnchor } from "./interview/extractTargetFromAnchor";
|
||||
import { buildSemanticMappings } from "./mapping/semanticMappingBuilder";
|
||||
import { ConfirmOverwriteModal } from "./ui/ConfirmOverwriteModal";
|
||||
import { GraphSchemaLoader } from "./schema/GraphSchemaLoader";
|
||||
import type { GraphSchema } from "./mapping/graphSchema";
|
||||
|
||||
export default class MindnetCausalAssistantPlugin extends Plugin {
|
||||
settings: MindnetSettings;
|
||||
|
|
@ -27,6 +29,8 @@ export default class MindnetCausalAssistantPlugin extends Plugin {
|
|||
private reloadDebounceTimer: number | null = null;
|
||||
private interviewConfig: InterviewConfig | null = null;
|
||||
private interviewConfigReloadDebounceTimer: number | null = null;
|
||||
private graphSchema: GraphSchema | null = null;
|
||||
private graphSchemaReloadDebounceTimer: number | null = null;
|
||||
|
||||
async onload(): Promise<void> {
|
||||
await this.loadSettings();
|
||||
|
|
@ -127,6 +131,22 @@ export default class MindnetCausalAssistantPlugin extends Plugin {
|
|||
this.interviewConfigReloadDebounceTimer = null;
|
||||
}, 200);
|
||||
}
|
||||
|
||||
// Check if modified file matches graph schema path
|
||||
const normalizedGraphSchemaPath = normalizeVaultPath(this.settings.graphSchemaPath);
|
||||
if (normalizedFilePath === normalizedGraphSchemaPath ||
|
||||
normalizedFilePath === `/${normalizedGraphSchemaPath}` ||
|
||||
normalizedFilePath.endsWith(`/${normalizedGraphSchemaPath}`)) {
|
||||
// Debounce reload
|
||||
if (this.graphSchemaReloadDebounceTimer !== null) {
|
||||
window.clearTimeout(this.graphSchemaReloadDebounceTimer);
|
||||
}
|
||||
|
||||
this.graphSchemaReloadDebounceTimer = window.setTimeout(async () => {
|
||||
await this.reloadGraphSchema();
|
||||
this.graphSchemaReloadDebounceTimer = null;
|
||||
}, 200);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
|
|
@ -384,7 +404,8 @@ export default class MindnetCausalAssistantPlugin extends Plugin {
|
|||
this.app,
|
||||
activeFile,
|
||||
this.settings,
|
||||
allowOverwrite
|
||||
allowOverwrite,
|
||||
this
|
||||
);
|
||||
|
||||
// Show summary
|
||||
|
|
@ -667,4 +688,58 @@ export default class MindnetCausalAssistantPlugin extends Plugin {
|
|||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure graph schema is loaded. Auto-loads if not present.
|
||||
* Returns GraphSchema instance or null on failure.
|
||||
*/
|
||||
public async ensureGraphSchemaLoaded(): Promise<GraphSchema | null> {
|
||||
if (this.graphSchema) {
|
||||
return this.graphSchema;
|
||||
}
|
||||
|
||||
try {
|
||||
this.graphSchema = await GraphSchemaLoader.load(
|
||||
this.app,
|
||||
this.settings.graphSchemaPath
|
||||
);
|
||||
|
||||
const ruleCount = this.graphSchema.schema.size;
|
||||
console.log(`Graph schema auto-loaded: ${ruleCount} source types`);
|
||||
return this.graphSchema;
|
||||
} catch (e) {
|
||||
const msg = e instanceof Error ? e.message : String(e);
|
||||
if (msg.includes("not found") || msg.includes("file not found")) {
|
||||
console.warn("Graph schema not found. Check the path in plugin settings.");
|
||||
} else {
|
||||
console.error(`Failed to load graph schema: ${msg}`);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload graph schema from file. Used by manual command and live reload.
|
||||
*/
|
||||
public async reloadGraphSchema(): Promise<void> {
|
||||
try {
|
||||
this.graphSchema = await GraphSchemaLoader.load(
|
||||
this.app,
|
||||
this.settings.graphSchemaPath
|
||||
);
|
||||
|
||||
const ruleCount = this.graphSchema.schema.size;
|
||||
console.log(`Graph schema reloaded: ${ruleCount} source types`);
|
||||
new Notice(`Graph schema reloaded: ${ruleCount} rules`);
|
||||
} catch (e) {
|
||||
const msg = e instanceof Error ? e.message : String(e);
|
||||
if (msg.includes("not found") || msg.includes("file not found")) {
|
||||
new Notice("Graph schema not found. Configure path in plugin settings.");
|
||||
} else {
|
||||
new Notice(`Failed to reload graph schema: ${msg}`);
|
||||
}
|
||||
console.error(e);
|
||||
this.graphSchema = null; // Clear cache on error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,8 @@ export async function buildSemanticMappings(
|
|||
app: App,
|
||||
file: TFile,
|
||||
settings: MindnetSettings,
|
||||
allowOverwrite: boolean
|
||||
allowOverwrite: boolean,
|
||||
plugin?: { ensureGraphSchemaLoaded?: () => Promise<GraphSchema | null> }
|
||||
): Promise<BuildResult> {
|
||||
const content = await app.vault.read(file);
|
||||
const lines = content.split(/\r?\n/);
|
||||
|
|
@ -64,6 +65,11 @@ export async function buildSemanticMappings(
|
|||
}
|
||||
|
||||
// Load graph schema (optional, continue if not found)
|
||||
if (plugin && plugin.ensureGraphSchemaLoaded) {
|
||||
// Use plugin's ensureGraphSchemaLoaded for caching and live reload
|
||||
graphSchema = await plugin.ensureGraphSchemaLoaded();
|
||||
} else {
|
||||
// Fallback: direct load (no caching)
|
||||
try {
|
||||
const schemaText = await VocabularyLoader.loadText(app, settings.graphSchemaPath);
|
||||
graphSchema = parseGraphSchema(schemaText);
|
||||
|
|
@ -72,6 +78,7 @@ export async function buildSemanticMappings(
|
|||
// Continue without schema
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get source type
|
||||
const sourceType = getSourceType(app, file);
|
||||
|
|
|
|||
30
src/schema/GraphSchemaLoader.ts
Normal file
30
src/schema/GraphSchemaLoader.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/**
|
||||
* Graph Schema loader with caching.
|
||||
*/
|
||||
|
||||
import { App, Notice } from "obsidian";
|
||||
import { normalizeVaultPath } from "../settings";
|
||||
import { parseGraphSchema, type GraphSchema } from "../mapping/graphSchema";
|
||||
import { VocabularyLoader } from "../vocab/VocabularyLoader";
|
||||
|
||||
export class GraphSchemaLoader {
|
||||
/**
|
||||
* Load graph schema from vault.
|
||||
*/
|
||||
static async load(app: App, vaultRelativePath: string): Promise<GraphSchema> {
|
||||
const text = await VocabularyLoader.loadText(app, vaultRelativePath);
|
||||
return parseGraphSchema(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that graph schema file exists in vault.
|
||||
*/
|
||||
static async validate(app: App, vaultRelativePath: string): Promise<boolean> {
|
||||
try {
|
||||
await VocabularyLoader.loadText(app, vaultRelativePath);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
15
src/tests/schema/GraphSchemaLoader.test.ts
Normal file
15
src/tests/schema/GraphSchemaLoader.test.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* Unit tests for GraphSchemaLoader.
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { GraphSchemaLoader } from "../../schema/GraphSchemaLoader";
|
||||
import { normalizeVaultPath } from "../../settings";
|
||||
|
||||
describe("GraphSchemaLoader", () => {
|
||||
it("should normalize vault paths correctly", () => {
|
||||
expect(normalizeVaultPath("_system/dictionary/graph_schema.md")).toBe("_system/dictionary/graph_schema.md");
|
||||
expect(normalizeVaultPath("_system\\dictionary\\graph_schema.md")).toBe("_system/dictionary/graph_schema.md");
|
||||
expect(normalizeVaultPath(" _system/dictionary/graph_schema.md ")).toBe("_system/dictionary/graph_schema.md");
|
||||
});
|
||||
});
|
||||
|
|
@ -64,6 +64,35 @@ export class MindnetSettingTab extends PluginSettingTab {
|
|||
this.plugin.settings.graphSchemaPath = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
)
|
||||
.addButton((button) =>
|
||||
button
|
||||
.setButtonText("Validate")
|
||||
.setCta()
|
||||
.onClick(async () => {
|
||||
try {
|
||||
const { GraphSchemaLoader } = await import("../schema/GraphSchemaLoader");
|
||||
const isValid = await GraphSchemaLoader.validate(
|
||||
this.app,
|
||||
this.plugin.settings.graphSchemaPath
|
||||
);
|
||||
if (isValid) {
|
||||
const schema = await GraphSchemaLoader.load(
|
||||
this.app,
|
||||
this.plugin.settings.graphSchemaPath
|
||||
);
|
||||
const ruleCount = schema.schema.size;
|
||||
new Notice(
|
||||
`Graph schema file found (${ruleCount} source types)`
|
||||
);
|
||||
} else {
|
||||
new Notice("Graph schema file not found or invalid");
|
||||
}
|
||||
} catch (e) {
|
||||
const msg = e instanceof Error ? e.message : String(e);
|
||||
new Notice(`Failed to validate graph schema: ${msg}`);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// Strict mode toggle
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user