obsidian-logfire/src/ui/settings-tab.ts
tolvitty ee599f42e7 Feature 8: Projections & Reports — Automatische Markdown-Reports
- types.ts: projections-Settings erweitert (enabled, outputFolder, dailyLog, sessionLog, weeklyDigest), heatmap zu BuiltinFormat
- formatters.ts: Query-Results → Markdown (Timeline, Table, Summary, Metric, Heatmap, Custom)
- Presets: daily-log (Tagesprotokoll), session-log (Session-Protokoll), weekly-digest (Wochenuebersicht)
- template-registry.ts: Built-in + Custom ProjectionTemplate Verwaltung
- projection-engine.ts: Kern-Engine mit Scheduling, Session-End-Listener, ProjectionPickerModal
- main.ts: ProjectionEngine Integration, 2 neue Commands (run-projection, run-all-projections)
- settings-tab.ts: Neue Sektion "Projektionen" mit Toggles und Konfiguration
- styles.css: ProjectionPickerModal Styling

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 11:52:06 +01:00

330 lines
12 KiB
TypeScript

import { App, PluginSettingTab, Setting, Notice } from 'obsidian';
import type LogfirePlugin from '../main';
export class LogfireSettingTab extends PluginSettingTab {
constructor(app: App, private plugin: LogfirePlugin) {
super(app, plugin);
}
display(): void {
const { containerEl } = this;
containerEl.empty();
// ----- General -----
containerEl.createEl('h2', { text: 'Allgemein' });
new Setting(containerEl)
.setName('Tracking aktiviert')
.setDesc('Hauptschalter für alle Event-Collector.')
.addToggle(t => t
.setValue(this.plugin.settings.general.enabled)
.onChange(async (v) => {
this.plugin.settings.general.enabled = v;
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Log-Ordner')
.setDesc('Ordner für Markdown-Projektionen. Wird automatisch vom Tracking ausgeschlossen.')
.addText(t => t
.setPlaceholder('Logfire')
.setValue(this.plugin.settings.general.logFolder)
.onChange(async (v) => {
this.plugin.settings.general.logFolder = v || 'Logfire';
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Beim Start pausieren')
.setDesc('Startet im pausierten Zustand — keine Events bis manuell fortgesetzt.')
.addToggle(t => t
.setValue(this.plugin.settings.general.pauseOnStartup)
.onChange(async (v) => {
this.plugin.settings.general.pauseOnStartup = v;
await this.plugin.saveSettings();
}));
// ----- Tracking -----
containerEl.createEl('h2', { text: 'Tracking' });
new Setting(containerEl)
.setName('Datei-Events')
.setDesc('Erstellen, Löschen, Umbenennen, Verschieben und Ändern von Dateien.')
.addToggle(t => t
.setValue(this.plugin.settings.tracking.fileEvents)
.onChange(async (v) => {
this.plugin.settings.tracking.fileEvents = v;
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Content-Analyse')
.setDesc('Semantische Diffs: Wortzählung, Links, Tags, Überschriften, Frontmatter.')
.addToggle(t => t
.setValue(this.plugin.settings.tracking.contentAnalysis)
.onChange(async (v) => {
this.plugin.settings.tracking.contentAnalysis = v;
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Navigation')
.setDesc('Aktive Datei-Wechsel, Datei öffnen/schließen mit Dauer.')
.addToggle(t => t
.setValue(this.plugin.settings.tracking.navigation)
.onChange(async (v) => {
this.plugin.settings.tracking.navigation = v;
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Editor-Änderungen')
.setDesc('Tastenanschläge (debounced und aggregiert).')
.addToggle(t => t
.setValue(this.plugin.settings.tracking.editorChanges)
.onChange(async (v) => {
this.plugin.settings.tracking.editorChanges = v;
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Kommando-Tracking')
.setDesc('Ausführung von Befehlen über Palette und Hotkeys.')
.addToggle(t => t
.setValue(this.plugin.settings.tracking.commandTracking)
.onChange(async (v) => {
this.plugin.settings.tracking.commandTracking = v;
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Ausgeschlossene Ordner')
.setDesc('Komma-getrennte Liste von Ordnern, die nicht getrackt werden.')
.addText(t => t
.setPlaceholder('.obsidian, templates')
.setValue(this.plugin.settings.tracking.excludedFolders.join(', '))
.onChange(async (v) => {
this.plugin.settings.tracking.excludedFolders = v
.split(',')
.map(s => s.trim())
.filter(s => s.length > 0);
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Ausgeschlossene Patterns')
.setDesc('Komma-getrennte Glob-Patterns (z.B. **/*.excalidraw.md).')
.addText(t => t
.setPlaceholder('**/*.excalidraw.md')
.setValue(this.plugin.settings.tracking.excludedPatterns.join(', '))
.onChange(async (v) => {
this.plugin.settings.tracking.excludedPatterns = v
.split(',')
.map(s => s.trim())
.filter(s => s.length > 0);
await this.plugin.saveSettings();
}));
// ----- Projections -----
containerEl.createEl('h2', { text: 'Projektionen' });
new Setting(containerEl)
.setName('Projektionen aktiviert')
.setDesc('Automatische Markdown-Reports aus Event-Daten.')
.addToggle(t => t
.setValue(this.plugin.settings.projections.enabled)
.onChange(async (v) => {
this.plugin.settings.projections.enabled = v;
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Output-Ordner')
.setDesc('Ordner für generierte Projektions-Dateien.')
.addText(t => t
.setPlaceholder('Logfire')
.setValue(this.plugin.settings.projections.outputFolder)
.onChange(async (v) => {
this.plugin.settings.projections.outputFolder = v || 'Logfire';
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Tagesprotokoll')
.setDesc('Automatisches Tagesprotokoll generieren.')
.addToggle(t => t
.setValue(this.plugin.settings.projections.dailyLog.enabled)
.onChange(async (v) => {
this.plugin.settings.projections.dailyLog.enabled = v;
await this.plugin.saveSettings();
}))
.addText(t => t
.setPlaceholder('23:00')
.setValue(this.plugin.settings.projections.dailyLog.time)
.onChange(async (v) => {
if (/^\d{2}:\d{2}$/.test(v)) {
this.plugin.settings.projections.dailyLog.time = v;
await this.plugin.saveSettings();
}
}));
new Setting(containerEl)
.setName('Session-Protokoll')
.setDesc('Protokoll bei Session-Ende generieren.')
.addToggle(t => t
.setValue(this.plugin.settings.projections.sessionLog.enabled)
.onChange(async (v) => {
this.plugin.settings.projections.sessionLog.enabled = v;
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Wochen-Digest')
.setDesc('Wöchentliche Zusammenfassung generieren.')
.addToggle(t => t
.setValue(this.plugin.settings.projections.weeklyDigest.enabled)
.onChange(async (v) => {
this.plugin.settings.projections.weeklyDigest.enabled = v;
await this.plugin.saveSettings();
}))
.addDropdown(d => d
.addOptions({
'0': 'Sonntag', '1': 'Montag', '2': 'Dienstag', '3': 'Mittwoch',
'4': 'Donnerstag', '5': 'Freitag', '6': 'Samstag',
})
.setValue(String(this.plugin.settings.projections.weeklyDigest.dayOfWeek))
.onChange(async (v) => {
this.plugin.settings.projections.weeklyDigest.dayOfWeek = parseInt(v, 10);
await this.plugin.saveSettings();
}));
// ----- Advanced (collapsible) -----
const advancedHeader = containerEl.createEl('details');
advancedHeader.createEl('summary', { text: 'Erweiterte Einstellungen' })
.style.cursor = 'pointer';
const advEl = advancedHeader.createDiv();
advEl.createEl('p', {
text: 'Diese Einstellungen beeinflussen Performance und Speicher. Die Standardwerte sind für die meisten Vaults optimal.',
cls: 'setting-item-description',
});
// Performance
advEl.createEl('h3', { text: 'Performance' });
new Setting(advEl)
.setName('Editor-Debounce (ms)')
.setDesc('Wartezeit nach letztem Tastenanschlag vor editor:change Event.')
.addText(t => t
.setValue(String(this.plugin.settings.advanced.debounceMs))
.onChange(async (v) => {
const n = parseInt(v, 10);
if (!isNaN(n) && n >= 500) {
this.plugin.settings.advanced.debounceMs = n;
await this.plugin.saveSettings();
}
}));
new Setting(advEl)
.setName('Flush-Intervall (ms)')
.setDesc('Wie oft gepufferte Events in die Datenbank geschrieben werden.')
.addText(t => t
.setValue(String(this.plugin.settings.advanced.flushIntervalMs))
.onChange(async (v) => {
const n = parseInt(v, 10);
if (!isNaN(n) && n >= 1000) {
this.plugin.settings.advanced.flushIntervalMs = n;
await this.plugin.saveSettings();
}
}));
new Setting(advEl)
.setName('Flush-Schwellwert')
.setDesc('Maximale Events im Puffer vor erzwungenem Schreiben.')
.addText(t => t
.setValue(String(this.plugin.settings.advanced.flushThreshold))
.onChange(async (v) => {
const n = parseInt(v, 10);
if (!isNaN(n) && n >= 10) {
this.plugin.settings.advanced.flushThreshold = n;
await this.plugin.saveSettings();
}
}));
// Retention
advEl.createEl('h3', { text: 'Aufbewahrung' });
new Setting(advEl)
.setName('Raw-Events aufbewahren (Tage)')
.setDesc('Danach werden Events in Tages-Statistiken aggregiert und gelöscht.')
.addText(t => t
.setValue(String(this.plugin.settings.advanced.retention.rawEventsDays))
.onChange(async (v) => {
const n = parseInt(v, 10);
if (!isNaN(n) && n >= 1) {
this.plugin.settings.advanced.retention.rawEventsDays = n;
await this.plugin.saveSettings();
}
}));
new Setting(advEl)
.setName('Tages-Statistiken aufbewahren (Tage)')
.setDesc('Danach werden Tages-Statistiken in Monats-Statistiken aggregiert.')
.addText(t => t
.setValue(String(this.plugin.settings.advanced.retention.dailyStatsDays))
.onChange(async (v) => {
const n = parseInt(v, 10);
if (!isNaN(n) && n >= 30) {
this.plugin.settings.advanced.retention.dailyStatsDays = n;
await this.plugin.saveSettings();
}
}));
new Setting(advEl)
.setName('Wartung beim Start')
.setDesc('Automatisch Retention-Cleanup beim Plugin-Start ausführen.')
.addToggle(t => t
.setValue(this.plugin.settings.advanced.retention.maintenanceOnStartup)
.onChange(async (v) => {
this.plugin.settings.advanced.retention.maintenanceOnStartup = v;
await this.plugin.saveSettings();
}));
// Database info
advEl.createEl('h3', { text: 'Datenbank' });
const dbInfoEl = advEl.createDiv();
try {
const eventCount = this.plugin.db.getEventCount();
const dbSize = this.plugin.db.getDatabaseSizeBytes();
const oldest = this.plugin.db.getOldestEventTimestamp();
dbInfoEl.createEl('p', {
text: `Events: ${eventCount.toLocaleString()} | Größe: ${formatBytes(dbSize)} | Ältestes: ${oldest ? new Date(oldest).toLocaleDateString() : 'k.A.'}`,
});
} catch {
dbInfoEl.createEl('p', { text: 'Datenbank-Info nicht verfügbar.' });
}
new Setting(advEl)
.setName('Datenbank-Aktionen')
.addButton(b => b
.setButtonText('Wartung ausführen')
.onClick(() => {
try {
this.plugin.db.runMaintenance(this.plugin.settings.advanced.retention);
new Notice('Logfire: Wartung abgeschlossen.');
this.display();
} catch (err) {
new Notice('Logfire: Wartung fehlgeschlagen.');
console.error('[Logfire] Wartung fehlgeschlagen:', err);
}
}));
}
}
function formatBytes(bytes: number): string {
if (bytes < 1024) return `${bytes} B`;
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
}