- 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>
330 lines
12 KiB
TypeScript
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`;
|
|
}
|