- Revamped the README to provide comprehensive documentation for the Mindnet Causal Assistant plugin, including user, administrator, developer, and architect guides. - Added specialized documentation sections for installation, deployment, and troubleshooting. - Enhanced the chain inspection logic to determine effective required links based on template definitions, profiles, and defaults, improving the accuracy of findings related to link completeness.
22 KiB
Mindnet Causal Assistant - Entwicklerhandbuch
Version: 1.0.0
Stand: 2025-01-XX
Zielgruppe: Entwickler, die am Plugin arbeiten oder es erweitern
Inhaltsverzeichnis
- Überblick
- Projekt-Struktur
- Entwicklungsumgebung
- Code-Architektur
- Hauptmodule
- Erweiterungen entwickeln
- Testing
- Build & Deployment
Überblick
Das Mindnet Causal Assistant Plugin ist ein Obsidian Community Plugin geschrieben in TypeScript, gebündelt mit esbuild zu einer einzelnen main.js Datei.
Technologie-Stack
- Sprache: TypeScript (strict mode)
- Bundler: esbuild
- Package Manager: npm
- Testing: Vitest
- Linting: ESLint mit obsidianmd Plugin
Projekt-Typ
- Obsidian Community Plugin
- Entry Point:
src/main.ts→main.js - Release-Artefakte:
main.js,manifest.json, optionalstyles.css
Projekt-Struktur
mindnet_obsidian/
├── src/ # TypeScript Source Code
│ ├── main.ts # Plugin Entry Point
│ ├── settings.ts # Settings Interface & Defaults
│ ├── analysis/ # Chain Inspector & Analysis
│ │ ├── chainInspector.ts # Haupt-Analyse-Engine
│ │ ├── templateMatching.ts # Template Matching Algorithmus
│ │ ├── graphIndex.ts # Graph-Indexierung
│ │ ├── sectionContext.ts # Section-Context-Handling
│ │ └── severityPolicy.ts # Severity-Policy-Logik
│ ├── commands/ # Command Implementations
│ │ ├── inspectChainsCommand.ts
│ │ └── fixFindingsCommand.ts
│ ├── dictionary/ # Config Loader & Parser
│ │ ├── ChainRolesLoader.ts
│ │ ├── ChainTemplatesLoader.ts
│ │ ├── DictionaryLoader.ts
│ │ ├── parseChainRoles.ts
│ │ ├── parseChainTemplates.ts
│ │ └── types.ts
│ ├── entityPicker/ # Entity Selection UI
│ │ ├── noteIndex.ts
│ │ ├── folderTree.ts
│ │ ├── filters.ts
│ │ ├── wikilink.ts
│ │ └── types.ts
│ ├── export/ # Graph Export
│ │ ├── exportGraph.ts
│ │ └── types.ts
│ ├── graph/ # Graph Building & Traversal
│ │ ├── GraphBuilder.ts
│ │ ├── GraphIndex.ts
│ │ ├── renderChainReport.ts
│ │ ├── resolveTarget.ts
│ │ └── traverse.ts
│ ├── interview/ # Interview Wizard
│ │ ├── InterviewConfigLoader.ts
│ │ ├── parseInterviewConfig.ts
│ │ ├── wizardState.ts
│ │ ├── loopState.ts
│ │ └── renderer.ts
│ ├── lint/ # Linting Engine
│ │ ├── LintEngine.ts
│ │ └── rules/
│ ├── mapping/ # Semantic Mapping
│ │ ├── semanticMappingBuilder.ts
│ │ ├── mappingExtractor.ts
│ │ ├── mappingBuilder.ts
│ │ └── edgeTypeSelector.ts
│ ├── parser/ # Markdown Parsing
│ │ ├── parseEdgesFromCallouts.ts
│ │ ├── parseFrontmatter.ts
│ │ └── parseRelLinks.ts
│ ├── schema/ # Graph Schema
│ ├── ui/ # UI Components
│ │ ├── InterviewWizardModal.ts
│ │ ├── ProfileSelectionModal.ts
│ │ ├── MindnetSettingTab.ts
│ │ └── ...
│ ├── unresolvedLink/ # Unresolved Link Handling
│ ├── vocab/ # Edge Vocabulary
│ └── tests/ # Test Files
├── docs/ # Dokumentation
├── scripts/ # Build Scripts
│ └── deploy-local.ps1
├── package.json # Dependencies & Scripts
├── tsconfig.json # TypeScript Config
├── esbuild.config.mjs # Build Config
├── manifest.json # Plugin Manifest
└── main.js # Generated Bundle (nicht committen)
Entwicklungsumgebung
Voraussetzungen
- Node.js: LTS Version (18+ empfohlen)
- npm: Inkludiert mit Node.js
- Git: Für Versionierung
- Obsidian Desktop: Für Testing
Setup
# Repository klonen
git clone <repository-url> mindnet_obsidian
cd mindnet_obsidian
# Dependencies installieren
npm install
# Development Build (Watch Mode)
npm run dev
# Production Build
npm run build
# Tests ausführen
npm run test
# Linting
npm run lint
Development Workflow
- Code ändern in
src/ - Watch Mode läuft (
npm run dev) - Automatisches Rebuild bei Dateiänderungen
- Deploy lokal (
npm run deploy:localodernpm run build:deploy) - Obsidian Plugin reload (disable/enable)
- Testen
Code-Architektur
Plugin Lifecycle
Entry Point: src/main.ts
export default class MindnetCausalAssistantPlugin extends Plugin {
settings: MindnetSettings;
async onload(): Promise<void> {
await this.loadSettings();
this.addSettingTab(new MindnetSettingTab(this.app, this));
// Register commands, event handlers, etc.
}
onunload(): void {
// Cleanup
}
}
Settings Management
Datei: src/settings.ts
export interface MindnetSettings {
edgeVocabularyPath: string;
graphSchemaPath: string;
// ... weitere Settings
}
export const DEFAULT_SETTINGS: MindnetSettings = {
edgeVocabularyPath: "_system/dictionary/edge_vocabulary.md",
// ... Defaults
};
Laden/Speichern:
async loadSettings(): Promise<void> {
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
}
async saveSettings(): Promise<void> {
await this.saveData(this.settings);
}
Command Registration
Pattern:
this.addCommand({
id: "mindnet-command-id",
name: "Mindnet: Command Name",
callback: async () => {
// Implementation
},
});
Editor Callbacks:
this.addCommand({
id: "mindnet-editor-command",
name: "Mindnet: Editor Command",
editorCallback: async (editor) => {
// Editor-basierte Implementation
},
});
Hauptmodule
1. Analysis (src/analysis/)
Zweck: Chain Inspector & Template Matching
Hauptdateien:
chainInspector.ts- Haupt-Analyse-EnginetemplateMatching.ts- Template Matching AlgorithmusgraphIndex.ts- Graph-Indexierung für TraversalsectionContext.ts- Section-Context-ExtraktionseverityPolicy.ts- Severity-Policy-Logik
Verwendung:
import { executeInspectChains } from "./commands/inspectChainsCommand";
await executeInspectChains(
app,
editor,
activeFile.path,
chainRoles,
settings,
{},
chainTemplates,
templatesLoadResult
);
2. Dictionary (src/dictionary/)
Zweck: Config Loading & Parsing
Hauptdateien:
ChainRolesLoader.ts- Lädt chain_roles.yamlChainTemplatesLoader.ts- Lädt chain_templates.yamlDictionaryLoader.ts- Basis-Loader mit Error-HandlingparseChainRoles.ts- YAML → ChainRolesConfig ParserparseChainTemplates.ts- YAML → ChainTemplatesConfig Parser
Pattern:
const result = await ChainRolesLoader.load(
app,
settings.chainRolesPath,
lastKnownGood
);
if (result.data !== null) {
// Use result.data
}
Last-Known-Good:
- Bei Fehlern wird letzte gültige Config verwendet
lastKnownGoodParameter für Fallback
3. Graph (src/graph/)
Zweck: Graph Building & Traversal
Hauptdateien:
GraphBuilder.ts- Baut Graph aus VaultGraphIndex.ts- Indexiert Edges für Traversaltraverse.ts- BFS/DFS TraversalresolveTarget.ts- Link-Target-AuflösungrenderChainReport.ts- Report-Generierung
Verwendung:
const graph = await buildGraph(app, vocabulary);
const index = buildIndex(graph.edges);
const paths = traverseForward(index, startId, maxHops, maxPaths);
4. Interview (src/interview/)
Zweck: Interview Wizard für Note-Erstellung
Hauptdateien:
InterviewConfigLoader.ts- Lädt interview_config.yamlparseInterviewConfig.ts- YAML → InterviewConfig ParserwizardState.ts- Wizard State Management (Steps, Loops, Nested Loops)loopState.ts- Loop State Management (nested loops)renderer.ts- Output-Rendering (Section-basiert)writeFrontmatter.ts- Frontmatter-GenerierungsectionKeyResolver.ts- Section-Key-AuflösungextractTargetFromAnchor.ts- Target-Extraktion aus Anchorsslugify.ts- Slug-Generierung für Dateinamentypes.ts- Interview-Types
Verwendung:
import { InterviewConfigLoader } from "./interview/InterviewConfigLoader";
import { writeFrontmatter } from "./interview/writeFrontmatter";
import { InterviewWizardModal } from "./ui/InterviewWizardModal";
// Config laden
const result = await InterviewConfigLoader.loadConfig(app, configPath);
const config = result.config;
// Frontmatter schreiben
const frontmatter = writeFrontmatter({
id: "note_123",
title: "Meine Note",
noteType: "experience",
interviewProfile: "experience_basic",
defaults: profile.defaults,
frontmatterWhitelist: config.frontmatterWhitelist,
});
// Wizard starten
const modal = new InterviewWizardModal(app, profile, onFinish);
modal.open();
Features:
- Nested Loops: Unterstützung für verschachtelte Loops
- Section-basierte Ausgabe: Output wird in Sections geschrieben
- Frontmatter-Whitelist: Erlaubte Frontmatter-Keys
- Post-Run Actions: Automatische Actions nach Wizard (z.B. Edger)
5. Mapping (src/mapping/)
Zweck: Semantic Mapping Builder
Hauptdateien:
semanticMappingBuilder.ts- Haupt-Mapping-BuildermappingExtractor.ts- Extrahiert existierende MappingsmappingBuilder.ts- Baut neue MappingsedgeTypeSelector.ts- Edge-Type-Selection UI
Verwendung:
import { buildSemanticMappings } from "./mapping/semanticMappingBuilder";
const result = await buildSemanticMappings(
app,
activeFile,
settings,
allowOverwrite,
plugin
);
6. Parser (src/parser/)
Zweck: Markdown Parsing
Hauptdateien:
parseEdgesFromCallouts.ts- Extrahiert Edges aus CalloutsparseFrontmatter.ts- Frontmatter-ParsingparseRelLinks.ts- Relative Link-Parsing
Verwendung:
import { parseEdgesFromCallouts } from "./parser/parseEdgesFromCallouts";
const edges = parseEdgesFromCallouts(content, file, vocabulary);
7. UI (src/ui/)
Zweck: UI Components (Modals, Views)
Hauptdateien:
InterviewWizardModal.ts- Interview Wizard UIProfileSelectionModal.ts- Profil-Auswahl UIMindnetSettingTab.ts- Settings TabEdgeTypeChooserModal.ts- Edge-Type-Auswahl UIEntityPickerModal.ts- Entity-Auswahl UIAdoptNoteModal.ts- Note-Adoption-ConfirmationFolderTreeModal.ts- Folder-Auswahl UILinkPromptModal.ts- Link-Eingabe UIInlineEdgeTypeModal.ts- Inline Edge-Type-SelectionConfirmOverwriteModal.ts- Overwrite-Confirmation
Pattern:
export class MyModal extends Modal {
constructor(app: App, onResult: (result: MyResult) => void) {
super(app);
this.onResult = onResult;
}
onOpen() {
// Build UI
}
onClose() {
// Cleanup
}
}
8. Vocabulary (src/vocab/)
Zweck: Edge Vocabulary Management
Hauptdateien:
Vocabulary.ts- Wrapper-Klasse für Lookup-MethodenVocabularyLoader.ts- Lädt Vocabulary-DateiparseEdgeVocabulary.ts- Parst Markdown zu EdgeVocabularytypes.ts- Vocabulary-Types
Verwendung:
import { VocabularyLoader } from "./vocab/VocabularyLoader";
import { parseEdgeVocabulary } from "./vocab/parseEdgeVocabulary";
import { Vocabulary } from "./vocab/Vocabulary";
const text = await VocabularyLoader.loadText(app, path);
const parsed = parseEdgeVocabulary(text);
const vocabulary = new Vocabulary(parsed);
const canonical = vocabulary.getCanonical("ausgelöst_durch");
const normalized = vocabulary.normalize("causes");
9. Lint (src/lint/)
Zweck: Linting Engine für Note-Validierung
Hauptdateien:
LintEngine.ts- Haupt-Linting-Enginerules/index.ts- Regel-Registryrules/rule_hub_has_causality.ts- Kausalitäts-Prüfungrules/rule_missing_target.ts- Fehlende Target-Prüfungrules/rule_unkown_edge.ts- Unbekannte Edge-Type-Prüfungtypes.ts- Lint-Types
Verwendung:
import { LintEngine } from "./lint/LintEngine";
const findings = await LintEngine.lintCurrentNote(
app,
vocabulary,
{ showCanonicalHints: true }
);
10. Export (src/export/)
Zweck: Graph Export zu JSON
Hauptdateien:
exportGraph.ts- Haupt-Export-Funktiontypes.ts- Export-Types
Verwendung:
import { exportGraph } from "./export/exportGraph";
await exportGraph(app, vocabulary, "_system/exports/graph.json");
11. Schema (src/schema/)
Zweck: Graph Schema Loading
Hauptdateien:
GraphSchemaLoader.ts- Lädt Graph-Schema-Datei
Verwendung:
import { GraphSchemaLoader } from "./schema/GraphSchemaLoader";
const schema = await GraphSchemaLoader.load(app, schemaPath);
const typical = schema.getTypicalEdgeTypes("experience");
12. Unresolved Link (src/unresolvedLink/)
Zweck: Unresolved Link Handling
Hauptdateien:
unresolvedLinkHandler.ts- Haupt-Handler für Link-ClickslinkHelpers.ts- Link-Parsing und -NormalisierungadoptHelpers.ts- Note-Adoption-Logik
Verwendung:
import { isUnresolvedLink, normalizeLinkTarget } from "./unresolvedLink/linkHelpers";
import { isAdoptCandidate, evaluateAdoptionConfidence } from "./unresolvedLink/adoptHelpers";
const unresolved = isUnresolvedLink(app, linkTarget, sourcePath);
const confidence = evaluateAdoptionConfidence(file, content, maxChars, hint, windowMs);
13. Entity Picker (src/entityPicker/)
Zweck: Entity Selection (Notes, Folders)
Hauptdateien:
noteIndex.ts- Baut Index aller NotesfolderTree.ts- Folder-Tree-Strukturfilters.ts- Filter-Logikwikilink.ts- Wikilink-Parsingtypes.ts- Entity-Picker-Types
Verwendung:
import { buildNoteIndex } from "./entityPicker/noteIndex";
import { buildFolderTree } from "./entityPicker/folderTree";
const index = await buildNoteIndex(app);
const tree = buildFolderTree(app);
14. Commands (src/commands/)
Zweck: Command Implementations
Hauptdateien:
inspectChainsCommand.ts- Chain Inspector CommandfixFindingsCommand.ts- Fix Findings Command
Verwendung:
import { executeInspectChains } from "./commands/inspectChainsCommand";
import { executeFixFindings } from "./commands/fixFindingsCommand";
await executeInspectChains(app, editor, filePath, chainRoles, settings, {}, chainTemplates, result);
await executeFixFindings(app, editor, filePath, chainRoles, interviewConfig, settings, plugin);
Erweiterungen entwickeln
1. Neuen Command hinzufügen
Schritt 1: Command in main.ts registrieren
this.addCommand({
id: "mindnet-my-command",
name: "Mindnet: My Command",
callback: async () => {
// Implementation
},
});
Schritt 2: Implementation in separater Datei (optional)
// src/commands/myCommand.ts
export async function executeMyCommand(
app: App,
settings: MindnetSettings
): Promise<void> {
// Implementation
}
Schritt 3: In main.ts importieren und verwenden
import { executeMyCommand } from "./commands/myCommand";
this.addCommand({
id: "mindnet-my-command",
name: "Mindnet: My Command",
callback: async () => {
await executeMyCommand(this.app, this.settings);
},
});
2. Neue Lint-Regel hinzufügen
Schritt 1: Regel-Datei erstellen
// src/lint/rules/rule_my_rule.ts
import type { LintRule, LintFinding } from "../types";
export const ruleMyRule: LintRule = {
id: "my_rule",
name: "My Rule",
severity: "WARN",
check: async (app, file, vocabulary): Promise<LintFinding[]> => {
const findings: LintFinding[] = [];
// Check logic
return findings;
},
};
Schritt 2: Regel registrieren
// src/lint/rules/index.ts
import { ruleMyRule } from "./rule_my_rule";
export const RULES = [
ruleMyRule,
// ... weitere Regeln
];
3. Neues Finding hinzufügen
Schritt 1: Finding-Code definieren
// In chainInspector.ts oder templateMatching.ts
const finding: Finding = {
code: "my_finding",
severity: "WARN",
message: "My finding message",
evidence: { /* ... */ },
};
Schritt 2: Finding generieren
if (condition) {
findings.push({
code: "my_finding",
severity: "WARN",
message: "My finding message",
evidence: { /* ... */ },
});
}
4. Neues UI-Element hinzufügen
Schritt 1: Modal/Component erstellen
// src/ui/MyModal.ts
import { Modal } from "obsidian";
export class MyModal extends Modal {
constructor(app: App, onResult: (result: MyResult) => void) {
super(app);
this.onResult = onResult;
}
onOpen() {
const { contentEl } = this;
// Build UI
}
onClose() {
const { contentEl } = this;
contentEl.empty();
}
}
Schritt 2: Modal verwenden
import { MyModal } from "./ui/MyModal";
new MyModal(this.app, (result) => {
// Handle result
}).open();
Testing
Test-Setup
Framework: Vitest
Konfiguration: vitest.config.ts
Test-Dateien: src/tests/**/*.test.ts
Tests ausführen
# Alle Tests
npm run test
# Watch Mode
npm run test -- --watch
# Spezifische Datei
npm run test -- src/tests/analysis/chainInspector.test.ts
Test-Pattern
import { describe, it, expect } from "vitest";
describe("MyModule", () => {
it("should do something", () => {
const result = myFunction(input);
expect(result).toBe(expected);
});
});
Mocking
Obsidian API Mocking:
// src/__mocks__/obsidian.ts
export const mockApp = {
vault: { /* ... */ },
metadataCache: { /* ... */ },
// ...
};
Build & Deployment
Development Build
npm run dev
Ergebnis:
main.jswird erstellt (mit Source Maps)- Watch Mode aktiviert
- Automatisches Rebuild bei Änderungen
Production Build
npm run build
Ergebnis:
main.jswird erstellt (minified, ohne Source Maps)- TypeScript-Check wird ausgeführt
- Tree-Shaking aktiviert
Lokales Deployment
Windows (PowerShell):
npm run deploy:local
# oder
powershell -ExecutionPolicy Bypass -File scripts/deploy-local.ps1
Script: scripts/deploy-local.ps1
- Kopiert
main.js,manifest.jsonnach Vault Plugin-Ordner - Optional:
styles.cssfalls vorhanden
Zielpfad:
<vault>/.obsidian/plugins/mindnet-causal-assistant/
Release-Prozess
-
Version bumpen:
npm version patch|minor|major- Aktualisiert
manifest.jsonundpackage.json - Fügt Eintrag zu
versions.jsonhinzu
- Aktualisiert
-
Build:
npm run build -
Git Commit:
git add manifest.json versions.json git commit -m "Release v1.0.1" git tag v1.0.1 -
GitHub Release:
- Erstelle Release mit Tag
v1.0.1(ohnevPrefix) - Upload
main.js,manifest.json, optionalstyles.css
- Erstelle Release mit Tag
Code-Standards
TypeScript
- Strict Mode: Aktiviert
- No Implicit Any: Aktiviert
- Strict Null Checks: Aktiviert
- Module Resolution: Node
Code-Organisation
- Ein Datei = Ein Verantwortungsbereich
- Klare Module-Grenzen
- Keine zirkulären Dependencies
- Tests neben Source-Code
Naming Conventions
- Dateien:
camelCase.ts(z.B.chainInspector.ts) - Klassen:
PascalCase(z.B.ChainInspector) - Funktionen:
camelCase(z.B.executeInspectChains) - Interfaces:
PascalCase(z.B.MindnetSettings) - Types:
PascalCase(z.B.ChainRolesConfig)
Kommentare
- JSDoc für öffentliche APIs
- Inline-Kommentare für komplexe Logik
- TODO-Kommentare für geplante Features
Debugging
Console-Logging
console.log("[Module] Message", data);
console.warn("[Module] Warning", data);
console.error("[Module] Error", data);
Debug-Settings
Settings: debugLogging: boolean
Verwendung:
if (this.settings.debugLogging) {
console.log("[Module] Debug info", data);
}
DevTools
Öffnen: Ctrl+Shift+I (Windows/Linux) oder Cmd+Option+I (Mac)
Console: Für Logs und Errors Network: Für API-Calls (falls vorhanden) Sources: Für Source Maps (Development Build)
Bekannte Einschränkungen
Obsidian API
- Mobile: Nicht alle APIs verfügbar
- Desktop-only:
isDesktopOnly: trueim Manifest - CodeMirror: Abhängig von Obsidian-Version
Performance
- Graph Building: Kann bei großen Vaults langsam sein
- Live-Reload: Debounced (200ms) für Performance
- Template Matching: BFS-Limit (max 30 Nodes)
Weitere Ressourcen
- Benutzerhandbuch: Endnutzer-Workflows
- Administratorhandbuch: Konfiguration und Wartung
- Architektur-Dokumentation: System-Übersicht
- Installation & Deployment: Setup-Anleitung
- Obsidian API Docs: https://docs.obsidian.md
Ende des Entwicklerhandbuchs