2026-01-15 11:34:29 +01:00
import { App , Notice , PluginSettingTab , Setting } from "obsidian" ;
import type MindnetCausalAssistantPlugin from "../main" ;
import { VocabularyLoader } from "../vocab/VocabularyLoader" ;
export class MindnetSettingTab extends PluginSettingTab {
plugin : MindnetCausalAssistantPlugin ;
constructor ( app : App , plugin : MindnetCausalAssistantPlugin ) {
super ( app , plugin ) ;
this . plugin = plugin ;
}
display ( ) : void {
const { containerEl } = this ;
containerEl . empty ( ) ;
containerEl . createEl ( "h2" , { text : "Mindnet Settings" } ) ;
2026-01-17 10:54:32 +01:00
// ============================================
// 1. Dictionary & Schema Configuration
// ============================================
containerEl . createEl ( "h3" , { text : "📚 Dictionary & Schema" } ) ;
containerEl . createEl ( "p" , {
text : "Konfigurationsdateien für Edge-Vokabular, Graph-Schema und Interview-Profile." ,
cls : "setting-item-description" ,
} ) ;
2026-01-15 11:34:29 +01:00
// Edge vocabulary path
new Setting ( containerEl )
. setName ( "Edge vocabulary path" )
2026-01-17 10:54:32 +01:00
. setDesc (
"Pfad zur Edge-Vokabular-Datei (Markdown). Definiert verfügbare Edge-Typen und deren Beschreibungen. Wird für die semantische Mapping-Erstellung verwendet."
)
2026-01-15 11:34:29 +01:00
. addText ( ( text ) = >
text
. setPlaceholder ( "_system/dictionary/edge_vocabulary.md" )
. setValue ( this . plugin . settings . edgeVocabularyPath )
. onChange ( async ( value ) = > {
this . plugin . settings . edgeVocabularyPath = value ;
await this . plugin . saveSettings ( ) ;
} )
)
. addButton ( ( button ) = >
button
. setButtonText ( "Validate" )
. setCta ( )
. onClick ( async ( ) = > {
try {
const text = await VocabularyLoader . loadText (
this . app ,
this . plugin . settings . edgeVocabularyPath
) ;
new Notice (
` Edge vocabulary file found ( ${ text . length } characters) `
) ;
} catch ( e ) {
const msg = e instanceof Error ? e.message : String ( e ) ;
new Notice (
` Failed to load edge vocabulary: ${ msg } `
) ;
}
} )
) ;
// Graph schema path
new Setting ( containerEl )
. setName ( "Graph schema path" )
2026-01-17 10:54:32 +01:00
. setDesc (
"Pfad zur Graph-Schema-Datei (Markdown). Definiert typische und verbotene Edge-Typen für verschiedene Quelltypen. Wird für Empfehlungen beim Mapping verwendet."
)
2026-01-15 11:34:29 +01:00
. addText ( ( text ) = >
text
. setPlaceholder ( "_system/dictionary/graph_schema.md" )
. setValue ( this . plugin . settings . graphSchemaPath )
. onChange ( async ( value ) = > {
this . plugin . settings . graphSchemaPath = value ;
await this . plugin . saveSettings ( ) ;
} )
2026-01-17 07:42:17 +01:00
)
. 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 } ` ) ;
}
} )
2026-01-15 11:34:29 +01:00
) ;
2026-01-17 10:54:32 +01:00
// Interview config path
2026-01-15 11:34:29 +01:00
new Setting ( containerEl )
2026-01-17 10:54:32 +01:00
. setName ( "Interview config path" )
. setDesc (
"Pfad zur Interview-Konfigurationsdatei (YAML). Definiert verfügbare Interview-Profile mit ihren Schritten und Einstellungen."
)
. addText ( ( text ) = >
text
. setPlaceholder ( "_system/dictionary/interview_config.yaml" )
. setValue ( this . plugin . settings . interviewConfigPath )
2026-01-15 11:34:29 +01:00
. onChange ( async ( value ) = > {
2026-01-17 10:54:32 +01:00
this . plugin . settings . interviewConfigPath = value ;
2026-01-15 11:34:29 +01:00
await this . plugin . saveSettings ( ) ;
} )
2026-01-17 10:54:32 +01:00
)
. addButton ( ( button ) = >
button
. setButtonText ( "Validate" )
. setCta ( )
. onClick ( async ( ) = > {
try {
const { InterviewConfigLoader } = await import ( "../interview/InterviewConfigLoader" ) ;
const result = await InterviewConfigLoader . loadConfig (
this . app ,
this . plugin . settings . interviewConfigPath
) ;
if ( result . errors . length > 0 ) {
new Notice (
` Interview config loaded with ${ result . errors . length } error(s). Check console. `
) ;
console . warn ( "Interview config errors:" , result . errors ) ;
} else {
new Notice (
` Interview config file found ( ${ result . config . profiles . length } profile(s)) `
) ;
}
} catch ( e ) {
const msg = e instanceof Error ? e.message : String ( e ) ;
new Notice ( ` Failed to load interview config: ${ msg } ` ) ;
}
} )
2026-01-15 11:34:29 +01:00
) ;
2026-01-17 10:54:32 +01:00
// ============================================
// 2. Graph Traversal & Linting
// ============================================
containerEl . createEl ( "h3" , { text : "🔍 Graph Traversal & Linting" } ) ;
containerEl . createEl ( "p" , {
text : "Einstellungen für Graph-Durchquerung und Lint-Validierung." ,
cls : "setting-item-description" ,
} ) ;
// Max hops
2026-01-15 11:34:29 +01:00
new Setting ( containerEl )
. setName ( "Max hops" )
2026-01-17 10:54:32 +01:00
. setDesc (
"Maximale Anzahl von Hops (Schritten) bei der Graph-Durchquerung. Bestimmt, wie tief der Graph durchsucht wird."
)
2026-01-15 11:34:29 +01:00
. addText ( ( text ) = >
text
. setPlaceholder ( "3" )
. setValue ( String ( this . plugin . settings . maxHops ) )
. onChange ( async ( value ) = > {
const numValue = parseInt ( value , 10 ) ;
if ( ! isNaN ( numValue ) && numValue > 0 ) {
this . plugin . settings . maxHops = numValue ;
await this . plugin . saveSettings ( ) ;
}
} )
) ;
2026-01-15 12:27:16 +01:00
2026-01-17 10:54:32 +01:00
// Chain direction
2026-01-15 12:27:16 +01:00
new Setting ( containerEl )
. setName ( "Chain direction" )
2026-01-17 10:54:32 +01:00
. setDesc (
"Richtung für Chain-Traversal: 'Forward' (vorwärts), 'Backward' (rückwärts) oder 'Both' (beide Richtungen)."
)
2026-01-15 12:27:16 +01:00
. addDropdown ( ( dropdown ) = >
dropdown
. addOption ( "forward" , "Forward" )
. addOption ( "backward" , "Backward" )
. addOption ( "both" , "Both" )
. setValue ( this . plugin . settings . chainDirection )
. onChange ( async ( value ) = > {
if ( value === "forward" || value === "backward" || value === "both" ) {
this . plugin . settings . chainDirection = value ;
await this . plugin . saveSettings ( ) ;
}
} )
) ;
2026-01-16 12:27:44 +01:00
2026-01-17 10:54:32 +01:00
// Strict mode
2026-01-16 12:27:44 +01:00
new Setting ( containerEl )
2026-01-17 10:54:32 +01:00
. setName ( "Strict mode" )
. setDesc (
"Aktiviert den strikten Validierungsmodus. Erzwingt strengere Regeln bei der Lint-Validierung."
)
. addToggle ( ( toggle ) = >
toggle
. setValue ( this . plugin . settings . strictMode )
2026-01-16 12:27:44 +01:00
. onChange ( async ( value ) = > {
2026-01-17 10:54:32 +01:00
this . plugin . settings . strictMode = value ;
2026-01-16 12:27:44 +01:00
await this . plugin . saveSettings ( ) ;
} )
2026-01-17 10:54:32 +01:00
) ;
// Show canonical hints
new Setting ( containerEl )
. setName ( "Show canonical hints" )
. setDesc (
"Zeigt INFO-Hinweise mit kanonischer Edge-Typ-Auflösung in den Lint-Ergebnissen an. Hilfreich für die Debugging von Edge-Typ-Zuordnungen."
2026-01-16 12:27:44 +01:00
)
2026-01-17 10:54:32 +01:00
. addToggle ( ( toggle ) = >
toggle
. setValue ( this . plugin . settings . showCanonicalHints )
. onChange ( async ( value ) = > {
this . plugin . settings . showCanonicalHints = value ;
await this . plugin . saveSettings ( ) ;
2026-01-16 12:27:44 +01:00
} )
) ;
2026-01-17 10:54:32 +01:00
// ============================================
// 3. Interview & Note Creation
// ============================================
containerEl . createEl ( "h3" , { text : "📝 Interview & Note Creation" } ) ;
containerEl . createEl ( "p" , {
text : "Einstellungen für Interview-Wizard und Notiz-Erstellung." ,
cls : "setting-item-description" ,
} ) ;
// Auto-start interview on create
2026-01-16 12:27:44 +01:00
new Setting ( containerEl )
. setName ( "Auto-start interview on create" )
2026-01-17 10:54:32 +01:00
. setDesc (
"Startet automatisch den Interview-Wizard, wenn eine neue Notiz über ein Profil erstellt wird."
)
2026-01-16 12:27:44 +01:00
. addToggle ( ( toggle ) = >
toggle
. setValue ( this . plugin . settings . autoStartInterviewOnCreate )
. onChange ( async ( value ) = > {
this . plugin . settings . autoStartInterviewOnCreate = value ;
await this . plugin . saveSettings ( ) ;
} )
) ;
2026-01-17 10:54:32 +01:00
// Default notes folder
new Setting ( containerEl )
. setName ( "Default notes folder" )
. setDesc (
"Standard-Ordner für neue Notizen (vault-relativer Pfad, leer für Root). Profil-spezifische Defaults haben Vorrang."
)
. addText ( ( text ) = >
text
. setPlaceholder ( "" )
. setValue ( this . plugin . settings . defaultNotesFolder )
. onChange ( async ( value ) = > {
this . plugin . settings . defaultNotesFolder = value || "" ;
await this . plugin . saveSettings ( ) ;
} )
) ;
// ============================================
// 4. Unresolved Link Handling
// ============================================
containerEl . createEl ( "h3" , { text : "🔗 Unresolved Link Handling" } ) ;
containerEl . createEl ( "p" , {
text : "Konfiguration für das Verhalten bei Klicks auf nicht aufgelöste Links." ,
cls : "setting-item-description" ,
} ) ;
// Intercept unresolved links
2026-01-16 12:27:44 +01:00
new Setting ( containerEl )
. setName ( "Intercept unresolved link clicks" )
2026-01-17 10:54:32 +01:00
. setDesc (
"Aktiviert die Abfangen von Klicks auf nicht aufgelöste interne Links. Öffnet die Profilauswahl, wenn auf einen nicht existierenden Link geklickt wird."
)
2026-01-16 12:27:44 +01:00
. addToggle ( ( toggle ) = >
toggle
. setValue ( this . plugin . settings . interceptUnresolvedLinkClicks )
. onChange ( async ( value ) = > {
this . plugin . settings . interceptUnresolvedLinkClicks = value ;
await this . plugin . saveSettings ( ) ;
} )
) ;
2026-01-17 07:27:11 +01:00
2026-01-17 10:54:32 +01:00
// Auto-start interview on unresolved click
2026-01-17 10:07:07 +01:00
new Setting ( containerEl )
. setName ( "Auto-start interview on unresolved link click" )
2026-01-17 10:54:32 +01:00
. setDesc (
"Startet automatisch den Interview-Wizard, wenn eine Notiz aus einem nicht aufgelösten Link erstellt wird. Erfordert, dass 'Intercept unresolved link clicks' aktiviert ist."
)
2026-01-17 10:07:07 +01:00
. addToggle ( ( toggle ) = >
toggle
. setValue ( this . plugin . settings . autoStartOnUnresolvedClick )
. onChange ( async ( value ) = > {
this . plugin . settings . autoStartOnUnresolvedClick = value ;
await this . plugin . saveSettings ( ) ;
} )
) ;
// Bypass modifier (Reading View)
new Setting ( containerEl )
. setName ( "Bypass modifier (Reading View)" )
2026-01-17 10:54:32 +01:00
. setDesc (
"Modifier-Taste zum Umgehen des Link-Intercepts in der Leseansicht. Wenn diese Taste gedrückt wird, wird der Standard-Link-Klick ausgeführt (z.B. zum Erstellen einer leeren Notiz)."
)
2026-01-17 10:07:07 +01:00
. addDropdown ( ( dropdown ) = >
dropdown
. addOption ( "Alt" , "Alt" )
. addOption ( "Ctrl" , "Ctrl/Cmd" )
. addOption ( "Shift" , "Shift" )
. addOption ( "None" , "None" )
. setValue ( this . plugin . settings . bypassModifier )
. onChange ( async ( value ) = > {
if ( value === "Alt" || value === "Ctrl" || value === "Shift" || value === "None" ) {
this . plugin . settings . bypassModifier = value ;
await this . plugin . saveSettings ( ) ;
}
} )
) ;
// Editor follow modifier (Live Preview/Source)
new Setting ( containerEl )
. setName ( "Editor follow modifier (Live Preview/Source)" )
2026-01-17 10:54:32 +01:00
. setDesc (
"Modifier-Taste, die im Editor (Live Preview/Source) gedrückt werden muss, um den Link-Intercept zu aktivieren. Standard: Ctrl/Cmd. Verhindert versehentliche Intercepts beim normalen Bearbeiten."
)
2026-01-17 10:07:07 +01:00
. addDropdown ( ( dropdown ) = >
dropdown
. addOption ( "Alt" , "Alt" )
. addOption ( "Ctrl" , "Ctrl/Cmd" )
. addOption ( "Shift" , "Shift" )
. addOption ( "None" , "None" )
. setValue ( this . plugin . settings . editorFollowModifier )
. onChange ( async ( value ) = > {
if ( value === "Alt" || value === "Ctrl" || value === "Shift" || value === "None" ) {
this . plugin . settings . editorFollowModifier = value ;
await this . plugin . saveSettings ( ) ;
}
} )
) ;
2026-01-17 10:54:32 +01:00
// ============================================
// 5. Templater Compatibility
// ============================================
containerEl . createEl ( "h3" , { text : "⚙️ Templater Compatibility" } ) ;
containerEl . createEl ( "p" , {
text : "Einstellungen für die Kompatibilität mit dem Templater-Plugin." ,
cls : "setting-item-description" ,
} ) ;
2026-01-17 10:07:07 +01:00
// Wait for first modify after create
new Setting ( containerEl )
. setName ( "Wait for first modify after create" )
2026-01-17 10:54:32 +01:00
. setDesc (
"Wartet, bis Templater die Datei modifiziert hat, bevor der Wizard gestartet wird. Empfohlen, wenn Templater verwendet wird, um Konflikte zu vermeiden."
)
2026-01-17 10:07:07 +01:00
. addToggle ( ( toggle ) = >
toggle
. setValue ( this . plugin . settings . waitForFirstModifyAfterCreate )
. onChange ( async ( value ) = > {
this . plugin . settings . waitForFirstModifyAfterCreate = value ;
await this . plugin . saveSettings ( ) ;
} )
) ;
// Modify timeout
new Setting ( containerEl )
. setName ( "Modify timeout (ms)" )
2026-01-17 10:54:32 +01:00
. setDesc (
"Timeout in Millisekunden für das Warten auf ein File-Modify-Event. Wenn Templater nicht innerhalb dieser Zeit reagiert, wird der Wizard trotzdem gestartet."
)
2026-01-17 10:07:07 +01:00
. addText ( ( text ) = >
text
. setPlaceholder ( "1200" )
. setValue ( String ( this . plugin . settings . waitForModifyTimeoutMs ) )
. onChange ( async ( value ) = > {
const numValue = parseInt ( value , 10 ) ;
if ( ! isNaN ( numValue ) && numValue > 0 ) {
this . plugin . settings . waitForModifyTimeoutMs = numValue ;
await this . plugin . saveSettings ( ) ;
}
} )
) ;
2026-01-17 10:54:32 +01:00
// ============================================
// 6. Note Adoption
// ============================================
containerEl . createEl ( "h3" , { text : "🔄 Note Adoption" } ) ;
containerEl . createEl ( "p" , {
text : "Einstellungen für die automatische Übernahme neu erstellter Notizen im Editor." ,
cls : "setting-item-description" ,
} ) ;
2026-01-17 10:07:07 +01:00
// Adopt new notes in editor
new Setting ( containerEl )
. setName ( "Adopt new notes in editor" )
2026-01-17 10:54:32 +01:00
. setDesc (
"Übernimmt automatisch neu erstellte Notizen im Editor und konvertiert sie in das Mindnet-Format (mit Frontmatter-ID). Nützlich, wenn Obsidian eine Notiz direkt erstellt, bevor unser Intercept greift."
)
2026-01-17 10:07:07 +01:00
. addToggle ( ( toggle ) = >
toggle
. setValue ( this . plugin . settings . adoptNewNotesInEditor )
. onChange ( async ( value ) = > {
this . plugin . settings . adoptNewNotesInEditor = value ;
await this . plugin . saveSettings ( ) ;
} )
) ;
// Adopt max chars
new Setting ( containerEl )
. setName ( "Adopt max chars" )
2026-01-17 10:54:32 +01:00
. setDesc (
"Maximale Inhaltslänge (in Zeichen), um eine Notiz als Übernahme-Kandidat zu betrachten. Leere oder sehr kurze Notizen werden bevorzugt übernommen."
)
2026-01-17 10:07:07 +01:00
. addText ( ( text ) = >
text
. setPlaceholder ( "200" )
. setValue ( String ( this . plugin . settings . adoptMaxChars ) )
. onChange ( async ( value ) = > {
const numValue = parseInt ( value , 10 ) ;
if ( ! isNaN ( numValue ) && numValue > 0 ) {
this . plugin . settings . adoptMaxChars = numValue ;
await this . plugin . saveSettings ( ) ;
}
} )
) ;
2026-01-17 10:11:30 +01:00
// Adopt confirm mode
new Setting ( containerEl )
. setName ( "Adopt confirm mode" )
2026-01-17 10:54:32 +01:00
. setDesc (
"Wann die Übernahme-Bestätigung angezeigt wird: 'Always ask' (immer fragen), 'Only for low confidence' (nur bei niedriger Konfidenz, überspringt bei hoher Konfidenz), oder 'Never ask' (nie fragen, automatisch übernehmen)."
)
2026-01-17 10:11:30 +01:00
. 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)" )
2026-01-17 10:54:32 +01:00
. setDesc (
"Zeitfenster in Millisekunden für die hohe Konfidenz-Übernahme. Notizen, die innerhalb dieses Zeitfensters nach einem Klick erstellt wurden, werden mit hoher Konfidenz als Übernahme-Kandidaten betrachtet."
)
2026-01-17 10:11:30 +01:00
. 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 ( ) ;
}
} )
) ;
2026-01-17 10:54:32 +01:00
// ============================================
// 7. Semantic Mapping Builder
// ============================================
containerEl . createEl ( "h3" , { text : "🕸️ Semantic Mapping Builder" } ) ;
containerEl . createEl ( "p" , {
text : "Einstellungen für den Semantic Mapping Builder, der automatisch Mapping-Blöcke in Notizen erstellt." ,
cls : "setting-item-description" ,
} ) ;
2026-01-17 07:27:11 +01:00
// Wrapper callout type
new Setting ( containerEl )
. setName ( "Mapping wrapper callout type" )
2026-01-17 10:54:32 +01:00
. setDesc (
"Callout-Typ für den Mapping-Wrapper (z.B. 'abstract', 'info', 'note'). Bestimmt das visuelle Erscheinungsbild des Mapping-Blocks."
)
2026-01-17 07:27:11 +01:00
. addText ( ( text ) = >
text
. setPlaceholder ( "abstract" )
. setValue ( this . plugin . settings . mappingWrapperCalloutType )
. onChange ( async ( value ) = > {
this . plugin . settings . mappingWrapperCalloutType = value || "abstract" ;
await this . plugin . saveSettings ( ) ;
} )
) ;
// Wrapper title
new Setting ( containerEl )
. setName ( "Mapping wrapper title" )
2026-01-17 10:54:32 +01:00
. setDesc (
"Titel-Text für den Mapping-Wrapper-Callout. Wird als Überschrift des Mapping-Blocks angezeigt."
)
2026-01-17 07:27:11 +01:00
. addText ( ( text ) = >
text
. setPlaceholder ( "🕸️ Semantic Mapping" )
. setValue ( this . plugin . settings . mappingWrapperTitle )
. onChange ( async ( value ) = > {
this . plugin . settings . mappingWrapperTitle = value || "🕸️ Semantic Mapping" ;
await this . plugin . saveSettings ( ) ;
} )
) ;
// Wrapper folded
new Setting ( containerEl )
. setName ( "Mapping wrapper folded" )
2026-01-17 10:54:32 +01:00
. setDesc (
"Startet mit dem Mapping-Wrapper-Callout eingeklappt (collapsed). Reduziert visuellen Lärm in der Notiz."
)
2026-01-17 07:27:11 +01:00
. addToggle ( ( toggle ) = >
toggle
. setValue ( this . plugin . settings . mappingWrapperFolded )
. onChange ( async ( value ) = > {
this . plugin . settings . mappingWrapperFolded = value ;
await this . plugin . saveSettings ( ) ;
} )
) ;
// Unassigned handling
new Setting ( containerEl )
. setName ( "Unassigned handling" )
2026-01-17 10:54:32 +01:00
. setDesc (
"Wie Links ohne bestehende Mappings behandelt werden: 'Prompt (interactive)' (interaktive Auswahl), 'None (skip unmapped)' (überspringen), 'Use default edge type' (Standard-Typ verwenden), oder 'Use advisor' (zukünftig: E1-Engine)."
)
2026-01-17 07:27:11 +01:00
. addDropdown ( ( dropdown ) = >
dropdown
. addOption ( "prompt" , "Prompt (interactive)" )
. addOption ( "none" , "None (skip unmapped)" )
. addOption ( "defaultType" , "Use default edge type" )
. addOption ( "advisor" , "Use advisor (future: E1 engine)" )
. setValue ( this . plugin . settings . unassignedHandling )
. onChange ( async ( value ) = > {
if ( value === "prompt" || value === "none" || value === "defaultType" || value === "advisor" ) {
this . plugin . settings . unassignedHandling = value ;
await this . plugin . saveSettings ( ) ;
}
} )
) ;
2026-01-17 10:54:32 +01:00
// Default edge type
new Setting ( containerEl )
. setName ( "Default edge type" )
. setDesc (
"Standard-Edge-Typ für nicht zugeordnete Links. Wird nur verwendet, wenn 'Unassigned handling' auf 'Use default edge type' gesetzt ist."
)
. addText ( ( text ) = >
text
. setPlaceholder ( "" )
. setValue ( this . plugin . settings . defaultEdgeType )
. onChange ( async ( value ) = > {
this . plugin . settings . defaultEdgeType = value ;
await this . plugin . saveSettings ( ) ;
} )
) ;
2026-01-17 07:27:11 +01:00
// Allow overwrite existing mappings
new Setting ( containerEl )
. setName ( "Allow overwrite existing mappings" )
2026-01-17 10:54:32 +01:00
. setDesc (
"Wenn aktiviert, wird vor dem Überschreiben bestehender Edge-Typ-Zuordnungen eine Bestätigung angefordert. Wenn deaktiviert, werden bestehende Mappings immer beibehalten."
)
2026-01-17 07:27:11 +01:00
. addToggle ( ( toggle ) = >
toggle
. setValue ( this . plugin . settings . allowOverwriteExistingMappings )
. onChange ( async ( value ) = > {
this . plugin . settings . allowOverwriteExistingMappings = value ;
await this . plugin . saveSettings ( ) ;
} )
) ;
2026-01-17 08:01:04 +01:00
2026-01-17 11:54:14 +01:00
// Inline micro edge suggester
new Setting ( containerEl )
. setName ( "Inline micro edge suggester enabled" )
. setDesc (
"Aktiviert den Inline-Micro-Edge-Suggester. Zeigt nach dem Einfügen eines Links über den Entity Picker sofort eine Edge-Typ-Auswahl an (nur wenn Profil edging.mode=inline_micro)."
)
. addToggle ( ( toggle ) = >
toggle
. setValue ( this . plugin . settings . inlineMicroEnabled )
. onChange ( async ( value ) = > {
this . plugin . settings . inlineMicroEnabled = value ;
await this . plugin . saveSettings ( ) ;
} )
) ;
// Inline max alternatives
new Setting ( containerEl )
. setName ( "Inline max alternatives" )
. setDesc (
"Maximale Anzahl von alternativen Edge-Typen, die im Inline-Micro-Modal angezeigt werden (Standard: 6)."
)
. addText ( ( text ) = >
text
. setPlaceholder ( "6" )
. setValue ( String ( this . plugin . settings . inlineMaxAlternatives ) )
. onChange ( async ( value ) = > {
const numValue = parseInt ( value , 10 ) ;
if ( ! isNaN ( numValue ) && numValue > 0 ) {
this . plugin . settings . inlineMaxAlternatives = numValue ;
await this . plugin . saveSettings ( ) ;
}
} )
) ;
2026-01-17 10:54:32 +01:00
// ============================================
// 8. Debug & Development
// ============================================
containerEl . createEl ( "h3" , { text : "🐛 Debug & Development" } ) ;
containerEl . createEl ( "p" , {
text : "Einstellungen für Debugging und Entwicklung." ,
cls : "setting-item-description" ,
} ) ;
// Debug logging
2026-01-17 08:01:04 +01:00
new Setting ( containerEl )
2026-01-17 10:54:32 +01:00
. setName ( "Debug logging" )
. setDesc (
"Aktiviert ausführliches Debug-Logging für das Unresolved-Link-Handling. Logs erscheinen in der Browser-Konsole (F12). Nützlich für die Fehlersuche."
)
. addToggle ( ( toggle ) = >
toggle
. setValue ( this . plugin . settings . debugLogging )
2026-01-17 08:01:04 +01:00
. onChange ( async ( value ) = > {
2026-01-17 10:54:32 +01:00
this . plugin . settings . debugLogging = value ;
2026-01-17 08:01:04 +01:00
await this . plugin . saveSettings ( ) ;
} )
) ;
2026-01-18 13:45:38 +01:00
// ============================================
// 9. Export
// ============================================
containerEl . createEl ( "h3" , { text : "📤 Export" } ) ;
containerEl . createEl ( "p" , {
text : "Einstellungen für den Graph-Export." ,
cls : "setting-item-description" ,
} ) ;
// Export path
new Setting ( containerEl )
. setName ( "Export path" )
. setDesc (
"Pfad für die exportierte Graph-JSON-Datei. Die Datei enthält alle Knoten (Nodes) und Kanten (Edges) aus dem Vault."
)
. addText ( ( text ) = >
text
. setPlaceholder ( "_system/exports/graph_export.json" )
. setValue ( this . plugin . settings . exportPath )
. onChange ( async ( value ) = > {
this . plugin . settings . exportPath = value || "_system/exports/graph_export.json" ;
await this . plugin . saveSettings ( ) ;
} )
)
. addButton ( ( button ) = >
button
. setButtonText ( "Export now" )
. setCta ( )
. onClick ( async ( ) = > {
try {
// Load vocabulary using the same method as the command
const { VocabularyLoader } = await import ( "../vocab/VocabularyLoader" ) ;
const { parseEdgeVocabulary } = await import ( "../vocab/parseEdgeVocabulary" ) ;
const { Vocabulary } = await import ( "../vocab/Vocabulary" ) ;
const text = await VocabularyLoader . loadText (
this . app ,
this . plugin . settings . edgeVocabularyPath
) ;
const parsed = parseEdgeVocabulary ( text ) ;
const vocabulary = new Vocabulary ( parsed ) ;
const { exportGraph } = await import ( "../export/exportGraph" ) ;
await exportGraph ( this . app , vocabulary , this . plugin . settings . exportPath ) ;
new Notice ( ` Graph exported to ${ this . plugin . settings . exportPath } ` ) ;
console . log ( ` Graph exported: ${ this . plugin . settings . exportPath } ` ) ;
} catch ( e ) {
const msg = e instanceof Error ? e.message : String ( e ) ;
new Notice ( ` Failed to export graph: ${ msg } ` ) ;
console . error ( e ) ;
}
} )
) ;
2026-01-15 11:34:29 +01:00
}
}