- Updated `parseEdgesFromCallouts.ts` to support edge extraction from both callout blocks and plain lines, allowing for more flexible edge definitions. - Introduced new settings in `settings.ts` for maximum assignments collected per template, enhancing loop protection during edge processing. - Enhanced documentation in `Entwicklerhandbuch.md` to reflect changes in edge parsing and settings. - Improved UI components to utilize the new settings for better user experience in the Chain Workbench and related features.
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.cmd run dev) - Automatisches Rebuild bei Dateiänderungen
- Deploy lokal (
npm.cmd run deploy:localodernpm.cmd 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 Callouts (inkl. Edges außerhalb des Abstract-Blocks oder als Plain-Zeilen[!edge] type+[[link]], damit die Zuordnung unabhängig von der Position funktioniert)parseFrontmatter.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.cmd run build:deploy
# 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