- Introduced new workflows for Chain Workbench and Vault Triage, enhancing user capabilities for managing template matches and identifying chain gaps. - Added commands for opening the Chain Workbench and scanning the vault for chain gaps, improving the overall functionality of the plugin. - Updated documentation to include detailed instructions for the new workflows, ensuring users can effectively utilize the features. - Enhanced the UI for both the Chain Workbench and Vault Triage Scan, providing a more intuitive user experience. - Implemented tests for the new functionalities to ensure reliability and accuracy in various scenarios.
915 lines
22 KiB
Markdown
915 lines
22 KiB
Markdown
# Mindnet Causal Assistant - Entwicklerhandbuch
|
|
|
|
> **Version:** 1.0.0
|
|
> **Stand:** 2025-01-XX
|
|
> **Zielgruppe:** Entwickler, die am Plugin arbeiten oder es erweitern
|
|
|
|
---
|
|
|
|
## Inhaltsverzeichnis
|
|
|
|
1. [Überblick](#überblick)
|
|
2. [Projekt-Struktur](#projekt-struktur)
|
|
3. [Entwicklungsumgebung](#entwicklungsumgebung)
|
|
4. [Code-Architektur](#code-architektur)
|
|
5. [Hauptmodule](#hauptmodule)
|
|
6. [Erweiterungen entwickeln](#erweiterungen-entwickeln)
|
|
7. [Testing](#testing)
|
|
8. [Build & Deployment](#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`, optional `styles.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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
1. **Code ändern** in `src/`
|
|
2. **Watch Mode** läuft (`npm.cmd run dev`)
|
|
3. **Automatisches Rebuild** bei Dateiänderungen
|
|
4. **Deploy lokal** (`npm.cmd run deploy:local` oder `npm.cmd run build:deploy`)
|
|
5. **Obsidian Plugin reload** (disable/enable)
|
|
6. **Testen**
|
|
|
|
---
|
|
|
|
## Code-Architektur
|
|
|
|
### Plugin Lifecycle
|
|
|
|
**Entry Point:** `src/main.ts`
|
|
|
|
```typescript
|
|
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`
|
|
|
|
```typescript
|
|
export interface MindnetSettings {
|
|
edgeVocabularyPath: string;
|
|
graphSchemaPath: string;
|
|
// ... weitere Settings
|
|
}
|
|
|
|
export const DEFAULT_SETTINGS: MindnetSettings = {
|
|
edgeVocabularyPath: "_system/dictionary/edge_vocabulary.md",
|
|
// ... Defaults
|
|
};
|
|
```
|
|
|
|
**Laden/Speichern:**
|
|
```typescript
|
|
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:**
|
|
```typescript
|
|
this.addCommand({
|
|
id: "mindnet-command-id",
|
|
name: "Mindnet: Command Name",
|
|
callback: async () => {
|
|
// Implementation
|
|
},
|
|
});
|
|
```
|
|
|
|
**Editor Callbacks:**
|
|
```typescript
|
|
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-Engine
|
|
- `templateMatching.ts` - Template Matching Algorithmus
|
|
- `graphIndex.ts` - Graph-Indexierung für Traversal
|
|
- `sectionContext.ts` - Section-Context-Extraktion
|
|
- `severityPolicy.ts` - Severity-Policy-Logik
|
|
|
|
**Verwendung:**
|
|
```typescript
|
|
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.yaml
|
|
- `ChainTemplatesLoader.ts` - Lädt chain_templates.yaml
|
|
- `DictionaryLoader.ts` - Basis-Loader mit Error-Handling
|
|
- `parseChainRoles.ts` - YAML → ChainRolesConfig Parser
|
|
- `parseChainTemplates.ts` - YAML → ChainTemplatesConfig Parser
|
|
|
|
**Pattern:**
|
|
```typescript
|
|
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
|
|
- `lastKnownGood` Parameter für Fallback
|
|
|
|
### 3. Graph (`src/graph/`)
|
|
|
|
**Zweck:** Graph Building & Traversal
|
|
|
|
**Hauptdateien:**
|
|
- `GraphBuilder.ts` - Baut Graph aus Vault
|
|
- `GraphIndex.ts` - Indexiert Edges für Traversal
|
|
- `traverse.ts` - BFS/DFS Traversal
|
|
- `resolveTarget.ts` - Link-Target-Auflösung
|
|
- `renderChainReport.ts` - Report-Generierung
|
|
|
|
**Verwendung:**
|
|
```typescript
|
|
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.yaml
|
|
- `parseInterviewConfig.ts` - YAML → InterviewConfig Parser
|
|
- `wizardState.ts` - Wizard State Management (Steps, Loops, Nested Loops)
|
|
- `loopState.ts` - Loop State Management (nested loops)
|
|
- `renderer.ts` - Output-Rendering (Section-basiert)
|
|
- `writeFrontmatter.ts` - Frontmatter-Generierung
|
|
- `sectionKeyResolver.ts` - Section-Key-Auflösung
|
|
- `extractTargetFromAnchor.ts` - Target-Extraktion aus Anchors
|
|
- `slugify.ts` - Slug-Generierung für Dateinamen
|
|
- `types.ts` - Interview-Types
|
|
|
|
**Verwendung:**
|
|
```typescript
|
|
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-Builder
|
|
- `mappingExtractor.ts` - Extrahiert existierende Mappings
|
|
- `mappingBuilder.ts` - Baut neue Mappings
|
|
- `edgeTypeSelector.ts` - Edge-Type-Selection UI
|
|
|
|
**Verwendung:**
|
|
```typescript
|
|
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
|
|
- `parseFrontmatter.ts` - Frontmatter-Parsing
|
|
- `parseRelLinks.ts` - Relative Link-Parsing
|
|
|
|
**Verwendung:**
|
|
```typescript
|
|
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 UI
|
|
- `ProfileSelectionModal.ts` - Profil-Auswahl UI
|
|
- `MindnetSettingTab.ts` - Settings Tab
|
|
- `EdgeTypeChooserModal.ts` - Edge-Type-Auswahl UI
|
|
- `EntityPickerModal.ts` - Entity-Auswahl UI
|
|
- `AdoptNoteModal.ts` - Note-Adoption-Confirmation
|
|
- `FolderTreeModal.ts` - Folder-Auswahl UI
|
|
- `LinkPromptModal.ts` - Link-Eingabe UI
|
|
- `InlineEdgeTypeModal.ts` - Inline Edge-Type-Selection
|
|
- `ConfirmOverwriteModal.ts` - Overwrite-Confirmation
|
|
|
|
**Pattern:**
|
|
```typescript
|
|
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-Methoden
|
|
- `VocabularyLoader.ts` - Lädt Vocabulary-Datei
|
|
- `parseEdgeVocabulary.ts` - Parst Markdown zu EdgeVocabulary
|
|
- `types.ts` - Vocabulary-Types
|
|
|
|
**Verwendung:**
|
|
```typescript
|
|
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-Engine
|
|
- `rules/index.ts` - Regel-Registry
|
|
- `rules/rule_hub_has_causality.ts` - Kausalitäts-Prüfung
|
|
- `rules/rule_missing_target.ts` - Fehlende Target-Prüfung
|
|
- `rules/rule_unkown_edge.ts` - Unbekannte Edge-Type-Prüfung
|
|
- `types.ts` - Lint-Types
|
|
|
|
**Verwendung:**
|
|
```typescript
|
|
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-Funktion
|
|
- `types.ts` - Export-Types
|
|
|
|
**Verwendung:**
|
|
```typescript
|
|
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:**
|
|
```typescript
|
|
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-Clicks
|
|
- `linkHelpers.ts` - Link-Parsing und -Normalisierung
|
|
- `adoptHelpers.ts` - Note-Adoption-Logik
|
|
|
|
**Verwendung:**
|
|
```typescript
|
|
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 Notes
|
|
- `folderTree.ts` - Folder-Tree-Struktur
|
|
- `filters.ts` - Filter-Logik
|
|
- `wikilink.ts` - Wikilink-Parsing
|
|
- `types.ts` - Entity-Picker-Types
|
|
|
|
**Verwendung:**
|
|
```typescript
|
|
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 Command
|
|
- `fixFindingsCommand.ts` - Fix Findings Command
|
|
|
|
**Verwendung:**
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
this.addCommand({
|
|
id: "mindnet-my-command",
|
|
name: "Mindnet: My Command",
|
|
callback: async () => {
|
|
// Implementation
|
|
},
|
|
});
|
|
```
|
|
|
|
**Schritt 2:** Implementation in separater Datei (optional)
|
|
|
|
```typescript
|
|
// src/commands/myCommand.ts
|
|
export async function executeMyCommand(
|
|
app: App,
|
|
settings: MindnetSettings
|
|
): Promise<void> {
|
|
// Implementation
|
|
}
|
|
```
|
|
|
|
**Schritt 3:** In `main.ts` importieren und verwenden
|
|
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
// 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
|
|
|
|
```typescript
|
|
// 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
|
|
|
|
```typescript
|
|
// In chainInspector.ts oder templateMatching.ts
|
|
const finding: Finding = {
|
|
code: "my_finding",
|
|
severity: "WARN",
|
|
message: "My finding message",
|
|
evidence: { /* ... */ },
|
|
};
|
|
```
|
|
|
|
**Schritt 2:** Finding generieren
|
|
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
// 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
|
|
|
|
```typescript
|
|
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
|
|
|
|
```bash
|
|
# Alle Tests
|
|
npm run test
|
|
|
|
# Watch Mode
|
|
npm run test -- --watch
|
|
|
|
# Spezifische Datei
|
|
npm run test -- src/tests/analysis/chainInspector.test.ts
|
|
```
|
|
|
|
### Test-Pattern
|
|
|
|
```typescript
|
|
import { describe, it, expect } from "vitest";
|
|
|
|
describe("MyModule", () => {
|
|
it("should do something", () => {
|
|
const result = myFunction(input);
|
|
expect(result).toBe(expected);
|
|
});
|
|
});
|
|
```
|
|
|
|
### Mocking
|
|
|
|
**Obsidian API Mocking:**
|
|
```typescript
|
|
// src/__mocks__/obsidian.ts
|
|
export const mockApp = {
|
|
vault: { /* ... */ },
|
|
metadataCache: { /* ... */ },
|
|
// ...
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
## Build & Deployment
|
|
|
|
### Development Build
|
|
|
|
```bash
|
|
npm run dev
|
|
```
|
|
|
|
**Ergebnis:**
|
|
- `main.js` wird erstellt (mit Source Maps)
|
|
- Watch Mode aktiviert
|
|
- Automatisches Rebuild bei Änderungen
|
|
|
|
### Production Build
|
|
|
|
```bash
|
|
npm run build
|
|
```
|
|
|
|
**Ergebnis:**
|
|
- `main.js` wird erstellt (minified, ohne Source Maps)
|
|
- TypeScript-Check wird ausgeführt
|
|
- Tree-Shaking aktiviert
|
|
|
|
### Lokales Deployment
|
|
|
|
**Windows (PowerShell):**
|
|
```bash
|
|
npm run deploy:local
|
|
# oder
|
|
powershell -ExecutionPolicy Bypass -File scripts/deploy-local.ps1
|
|
```
|
|
|
|
**Script:** `scripts/deploy-local.ps1`
|
|
- Kopiert `main.js`, `manifest.json` nach Vault Plugin-Ordner
|
|
- Optional: `styles.css` falls vorhanden
|
|
|
|
**Zielpfad:**
|
|
```
|
|
<vault>/.obsidian/plugins/mindnet-causal-assistant/
|
|
```
|
|
|
|
### Release-Prozess
|
|
|
|
1. **Version bumpen:**
|
|
```bash
|
|
npm version patch|minor|major
|
|
```
|
|
- Aktualisiert `manifest.json` und `package.json`
|
|
- Fügt Eintrag zu `versions.json` hinzu
|
|
|
|
2. **Build:**
|
|
```bash
|
|
npm run build
|
|
```
|
|
|
|
3. **Git Commit:**
|
|
```bash
|
|
git add manifest.json versions.json
|
|
git commit -m "Release v1.0.1"
|
|
git tag v1.0.1
|
|
```
|
|
|
|
4. **GitHub Release:**
|
|
- Erstelle Release mit Tag `v1.0.1` (ohne `v` Prefix)
|
|
- Upload `main.js`, `manifest.json`, optional `styles.css`
|
|
|
|
---
|
|
|
|
## 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
|
|
|
|
```typescript
|
|
console.log("[Module] Message", data);
|
|
console.warn("[Module] Warning", data);
|
|
console.error("[Module] Error", data);
|
|
```
|
|
|
|
### Debug-Settings
|
|
|
|
**Settings:** `debugLogging: boolean`
|
|
|
|
**Verwendung:**
|
|
```typescript
|
|
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: true` im 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**
|