diff --git a/src/main.ts b/src/main.ts index c6991db..fbbdcba 100644 --- a/src/main.ts +++ b/src/main.ts @@ -17,6 +17,10 @@ import { registerLogfireBlock, registerLogfireSqlBlock, registerLogfireDashboard import { QueryModal } from './query/query-modal'; import { VirtualTableManager } from './query/virtual-tables'; import { DashboardView, DASHBOARD_VIEW_TYPE } from './viz/dashboard'; +import { HistoryManager } from './management/history'; +import { FavoritesManager, SaveFavoriteModal } from './management/favorites'; +import { TemplateManager, TemplatePickerModal } from './management/templates'; +import { SchemaView, SCHEMA_VIEW_TYPE } from './ui/schema-view'; export default class LogfirePlugin extends Plugin { settings!: LogfireSettings; @@ -24,6 +28,9 @@ export default class LogfirePlugin extends Plugin { eventBus!: EventBus; sessionManager!: SessionManager; contentAnalyzer!: ContentAnalyzer; + historyManager!: HistoryManager; + favoritesManager!: FavoritesManager; + templateManager!: TemplateManager; private fileCollector!: FileCollector; private contentCollector!: ContentCollector; @@ -53,6 +60,11 @@ export default class LogfirePlugin extends Plugin { const vaultName = this.app.vault.getName(); this.sessionManager = new SessionManager(this.eventBus, this.db, vaultName); + // Query management + this.historyManager = new HistoryManager(); + this.favoritesManager = new FavoritesManager(); + this.templateManager = new TemplateManager(); + // Content analyzer this.contentAnalyzer = new ContentAnalyzer(); if (this.db.hasBaseline()) { @@ -97,6 +109,12 @@ export default class LogfirePlugin extends Plugin { (leaf) => new DashboardView(leaf, this), ); + // UI: Schema-Browser view + this.registerView( + SCHEMA_VIEW_TYPE, + (leaf) => new SchemaView(leaf, this.db), + ); + // UI: Status bar this.statusBar = new StatusBar(this); this.statusBar.start(); @@ -321,7 +339,31 @@ export default class LogfirePlugin extends Plugin { id: 'open-query', name: 'Query-Editor \u00f6ffnen', callback: () => { - new QueryModal(this.app, this.db).open(); + new QueryModal(this.app, this.db, this.historyManager).open(); + }, + }); + + this.addCommand({ + id: 'show-schema', + name: 'Schema-Browser anzeigen', + callback: () => this.activateSchema(), + }); + + this.addCommand({ + id: 'show-templates', + name: 'Query-Templates anzeigen', + callback: () => { + new TemplatePickerModal(this, this.templateManager, (sql) => { + new QueryModal(this.app, this.db, this.historyManager, sql).open(); + }).open(); + }, + }); + + this.addCommand({ + id: 'save-favorite', + name: 'Aktuelle Query als Favorit speichern', + callback: () => { + new SaveFavoriteModal(this, this.favoritesManager, '').open(); }, }); @@ -376,6 +418,23 @@ export default class LogfirePlugin extends Plugin { } } + // --------------------------------------------------------------------------- + // Schema view + // --------------------------------------------------------------------------- + + private async activateSchema(): Promise { + const existing = this.app.workspace.getLeavesOfType(SCHEMA_VIEW_TYPE); + if (existing.length > 0) { + this.app.workspace.revealLeaf(existing[0]); + return; + } + const leaf = this.app.workspace.getRightLeaf(false); + if (leaf) { + await leaf.setViewState({ type: SCHEMA_VIEW_TYPE, active: true }); + this.app.workspace.revealLeaf(leaf); + } + } + // --------------------------------------------------------------------------- // Settings // --------------------------------------------------------------------------- diff --git a/styles.css b/styles.css index d12233c..bdae9eb 100644 --- a/styles.css +++ b/styles.css @@ -924,3 +924,276 @@ padding: 0 0 8px 0; letter-spacing: -0.01em; } + +/* ═══════════════════════════════════════════════════════════════════════════ + Schema Browser — Database Introspection Sidebar + ═══════════════════════════════════════════════════════════════════════════ */ + +.logfire-schema-view { + display: flex; + flex-direction: column; + height: 100%; + overflow: hidden; + background: var(--background-primary); +} + +.logfire-schema-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + padding: 8px 10px; + border-bottom: 1px solid var(--background-modifier-border); + background: var(--background-secondary); + flex-shrink: 0; +} + +.logfire-schema-title { + font-family: var(--font-monospace); + font-size: 13px; + font-weight: 600; + color: var(--text-normal); + letter-spacing: -0.01em; +} + +.logfire-schema-content { + flex: 1; + overflow-y: auto; + padding: 4px 0; + font-family: var(--font-monospace); + font-size: 11.5px; +} + +.logfire-schema-content::-webkit-scrollbar { + width: 6px; +} + +.logfire-schema-content::-webkit-scrollbar-track { + background: transparent; +} + +.logfire-schema-content::-webkit-scrollbar-thumb { + background: var(--background-modifier-border); + border-radius: 3px; +} + +/* --------------------------------------------------------------------------- + Schema — Table Node + --------------------------------------------------------------------------- */ + +.logfire-schema-table { + border-bottom: 1px solid color-mix(in srgb, var(--background-modifier-border) 40%, transparent); +} + +.logfire-schema-table-header { + display: flex; + align-items: center; + gap: 5px; + padding: 4px 10px; + cursor: pointer; + transition: background 60ms ease; + user-select: none; +} + +.logfire-schema-table-header:hover { + background: var(--background-secondary); +} + +.logfire-schema-arrow { + color: var(--text-faint); + font-size: 9px; + width: 10px; + flex-shrink: 0; +} + +.logfire-schema-table-name { + font-weight: 600; + color: var(--text-accent); +} + +.logfire-schema-row-count { + color: var(--text-faint); + font-size: 10px; + margin-left: auto; + font-variant-numeric: tabular-nums; +} + +/* --------------------------------------------------------------------------- + Schema — Column & Index Details + --------------------------------------------------------------------------- */ + +.logfire-schema-details { + padding: 2px 0 4px 20px; +} + +.logfire-schema-col { + display: flex; + align-items: center; + gap: 6px; + padding: 1px 10px; + color: var(--text-normal); + line-height: 1.6; +} + +.logfire-schema-col:hover { + background: color-mix(in srgb, var(--background-secondary) 40%, transparent); +} + +.logfire-schema-col-name { + flex-shrink: 0; +} + +.logfire-schema-col-type { + color: var(--text-faint); + font-size: 10px; + margin-left: auto; + text-transform: uppercase; +} + +.logfire-schema-section-header { + padding: 4px 10px 1px; + font-size: 10px; + font-weight: 600; + letter-spacing: 0.03em; + text-transform: uppercase; + color: var(--text-muted); + margin-top: 4px; +} + +.logfire-schema-idx { + display: flex; + align-items: center; + gap: 6px; + padding: 1px 10px; + color: var(--text-muted); + font-size: 10.5px; + line-height: 1.6; +} + +.logfire-schema-idx-cols { + color: var(--text-faint); + font-size: 10px; + margin-left: auto; +} + +/* ═══════════════════════════════════════════════════════════════════════════ + Template Picker & Favorite Modals + ═══════════════════════════════════════════════════════════════════════════ */ + +.logfire-template-picker h2, +.logfire-save-fav-modal h3 { + font-family: var(--font-monospace); + font-size: 16px; + font-weight: 700; + letter-spacing: -0.01em; + margin-bottom: 10px; + color: var(--text-normal); +} + +.logfire-template-list { + max-height: 400px; + overflow-y: auto; +} + +.logfire-template-item { + padding: 8px 10px; + border-bottom: 1px solid var(--background-modifier-border); + transition: background 60ms ease; +} + +.logfire-template-item:hover { + background: color-mix(in srgb, var(--background-secondary) 40%, transparent); +} + +.logfire-template-item:last-child { + border-bottom: none; +} + +.logfire-template-header { + display: flex; + align-items: center; + gap: 6px; + margin-bottom: 2px; +} + +.logfire-template-name { + font-family: var(--font-monospace); + font-size: 12px; + font-weight: 600; + color: var(--text-normal); +} + +.logfire-template-badge { + font-family: var(--font-monospace); + font-size: 9px; + letter-spacing: 0.04em; + text-transform: uppercase; + padding: 1px 5px; + border-radius: 3px; + background: color-mix(in srgb, var(--interactive-accent) 15%, transparent); + color: var(--interactive-accent); +} + +.logfire-template-desc { + font-family: var(--font-monospace); + font-size: 11px; + color: var(--text-muted); + margin-bottom: 4px; +} + +.logfire-template-preview { + font-family: var(--font-monospace); + font-size: 10.5px; + line-height: 1.4; + color: var(--text-faint); + background: var(--background-secondary); + border-radius: 3px; + padding: 4px 8px; + margin: 4px 0; + overflow: hidden; + white-space: pre-wrap; +} + +.logfire-template-actions { + display: flex; + gap: 6px; + margin-top: 6px; +} + +.logfire-template-actions button { + font-family: var(--font-monospace); + font-size: 11px; + letter-spacing: 0.02em; + text-transform: uppercase; +} + +/* --------------------------------------------------------------------------- + Modal Shared — Buttons & Preview + --------------------------------------------------------------------------- */ + +.logfire-modal-buttons { + display: flex; + justify-content: flex-end; + gap: 8px; + margin-top: 16px; +} + +.logfire-modal-buttons button { + font-family: var(--font-monospace); + font-size: 12px; + letter-spacing: 0.02em; +} + +.logfire-sql-preview { + font-family: var(--font-monospace); + font-size: 11px; + line-height: 1.4; + color: var(--text-muted); + background: var(--background-secondary); + border-radius: 4px; + padding: 6px 10px; + margin: 4px 0; + white-space: pre-wrap; + max-height: 120px; + overflow: auto; +}