diff --git a/src/main.ts b/src/main.ts index ad32f96..ac85ed9 100644 --- a/src/main.ts +++ b/src/main.ts @@ -13,6 +13,8 @@ import { LogfireSettingTab } from './ui/settings-tab'; import { InitialScanModal } from './ui/initial-scan-modal'; import { StatusBar } from './ui/status-bar'; import { EventStreamView, EVENT_STREAM_VIEW_TYPE } from './ui/event-stream-view'; +import { registerLogfireBlock, registerLogfireSqlBlock, cleanupAllRefreshTimers } from './query/processor'; +import { QueryModal } from './query/query-modal'; export default class LogfirePlugin extends Plugin { settings!: LogfireSettings; @@ -90,6 +92,14 @@ export default class LogfirePlugin extends Plugin { this.statusBar = new StatusBar(this); this.statusBar.start(); + // Query: Code-Block-Prozessoren + registerLogfireBlock(this.db, (lang, handler) => { + this.registerMarkdownCodeBlockProcessor(lang, handler); + }); + registerLogfireSqlBlock(this.db, (lang, handler) => { + this.registerMarkdownCodeBlockProcessor(lang, handler); + }); + // Ribbon icon this.addRibbonIcon('activity', 'Logfire: Event-Stream', () => { this.activateEventStream(); @@ -125,6 +135,7 @@ export default class LogfirePlugin extends Plugin { async onunload(): Promise { console.log('[Logfire] Entlade Plugin...'); + cleanupAllRefreshTimers(); this.statusBar?.destroy(); this.stopTracking(); @@ -271,6 +282,14 @@ export default class LogfirePlugin extends Plugin { }, }); + this.addCommand({ + id: 'open-query', + name: 'Query-Editor \u00f6ffnen', + callback: () => { + new QueryModal(this.app, this.db).open(); + }, + }); + this.addCommand({ id: 'debug-info', name: 'Debug-Info', diff --git a/styles.css b/styles.css index 18f9042..6c23a8a 100644 --- a/styles.css +++ b/styles.css @@ -314,3 +314,368 @@ font-size: 12px; letter-spacing: 0.02em; } + +/* ═══════════════════════════════════════════════════════════════════════════ + Query Modal — SQL Console + ═══════════════════════════════════════════════════════════════════════════ */ + +.logfire-query-modal { + font-family: var(--font-monospace); +} + +.logfire-query-modal h2 { + font-size: 16px; + font-weight: 700; + letter-spacing: -0.01em; + margin-bottom: 10px; + color: var(--text-normal); +} + +/* --------------------------------------------------------------------------- + Query Modal — Toolbar + --------------------------------------------------------------------------- */ + +.logfire-qm-toolbar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + margin-bottom: 8px; +} + +.logfire-qm-toolbar button { + padding: 3px 10px; + border: 1px solid var(--background-modifier-border); + border-radius: 4px; + background: var(--background-primary); + color: var(--text-muted); + font-family: var(--font-monospace); + font-size: 11px; + letter-spacing: 0.02em; + text-transform: uppercase; + cursor: pointer; + transition: background 100ms ease, color 100ms ease, border-color 100ms ease; +} + +.logfire-qm-toolbar button:hover { + background: var(--background-modifier-hover); + color: var(--text-normal); + border-color: var(--text-faint); +} + +.logfire-qm-hint { + color: var(--text-faint); + font-size: 10px; + letter-spacing: 0.03em; + text-transform: uppercase; +} + +/* --------------------------------------------------------------------------- + Query Modal — Editor + --------------------------------------------------------------------------- */ + +.logfire-qm-editor { + display: block; + width: 100%; + min-height: 100px; + max-height: 200px; + padding: 8px 10px; + border: 1px solid var(--background-modifier-border); + border-radius: 4px; + background: var(--background-primary); + color: var(--text-normal); + font-family: var(--font-monospace); + font-size: 12px; + line-height: 1.5; + resize: vertical; + outline: none; + tab-size: 2; + transition: border-color 120ms ease; +} + +.logfire-qm-editor:focus { + border-color: var(--interactive-accent); +} + +.logfire-qm-editor::placeholder { + color: var(--text-faint); + font-style: italic; +} + +/* --------------------------------------------------------------------------- + Query Modal — Buttons + --------------------------------------------------------------------------- */ + +.logfire-qm-buttons { + display: flex; + gap: 6px; + margin-top: 8px; + margin-bottom: 12px; + flex-wrap: wrap; +} + +.logfire-qm-buttons button { + padding: 4px 12px; + border: 1px solid var(--background-modifier-border); + border-radius: 4px; + background: var(--background-primary); + color: var(--text-muted); + font-family: var(--font-monospace); + font-size: 11px; + letter-spacing: 0.02em; + text-transform: uppercase; + cursor: pointer; + transition: background 100ms ease, color 100ms ease, border-color 100ms ease; +} + +.logfire-qm-buttons button:hover { + background: var(--background-modifier-hover); + color: var(--text-normal); + border-color: var(--text-faint); +} + +.logfire-qm-buttons button.mod-cta { + background: var(--interactive-accent); + color: var(--text-on-accent); + border-color: var(--interactive-accent); +} + +.logfire-qm-buttons button.mod-cta:hover { + filter: brightness(1.1); +} + +/* --------------------------------------------------------------------------- + Query Modal — Results + --------------------------------------------------------------------------- */ + +.logfire-qm-results { + max-height: 360px; + overflow: auto; + border: 1px solid var(--background-modifier-border); + border-radius: 4px; + background: var(--background-primary); +} + +.logfire-qm-results:empty { + display: none; +} + +.logfire-qm-results::-webkit-scrollbar { + width: 6px; + height: 6px; +} + +.logfire-qm-results::-webkit-scrollbar-track { + background: transparent; +} + +.logfire-qm-results::-webkit-scrollbar-thumb { + background: var(--background-modifier-border); + border-radius: 3px; +} + +.logfire-qm-results::-webkit-scrollbar-thumb:hover { + background: var(--text-faint); +} + +.logfire-qm-truncated { + padding: 4px 10px; + font-size: 10px; + color: var(--text-faint); + letter-spacing: 0.02em; + text-transform: uppercase; + border-top: 1px solid var(--background-modifier-border); +} + +/* ═══════════════════════════════════════════════════════════════════════════ + Data Table — Inline Query Results + ═══════════════════════════════════════════════════════════════════════════ */ + +.logfire-table { + width: 100%; + border-collapse: collapse; + font-family: var(--font-monospace); + font-size: 11.5px; + line-height: 1.4; + font-variant-numeric: tabular-nums; +} + +.logfire-table thead th { + padding: 5px 10px; + text-align: left; + font-weight: 600; + font-size: 10px; + letter-spacing: 0.04em; + text-transform: uppercase; + color: var(--text-muted); + background: var(--background-secondary); + border-bottom: 2px solid var(--background-modifier-border); + position: sticky; + top: 0; + z-index: 1; + white-space: nowrap; +} + +.logfire-table tbody td { + padding: 3px 10px; + color: var(--text-normal); + border-bottom: 1px solid color-mix(in srgb, var(--background-modifier-border) 50%, transparent); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 300px; +} + +.logfire-table tbody tr:nth-child(even) { + background: color-mix(in srgb, var(--background-secondary) 30%, transparent); +} + +.logfire-table tbody tr:hover { + background: var(--background-secondary); +} + +.logfire-table tbody tr:hover td { + border-bottom-color: var(--background-modifier-border); +} + +/* --------------------------------------------------------------------------- + Timeline + --------------------------------------------------------------------------- */ + +.logfire-timeline { + list-style: none; + padding: 0; + margin: 0; + font-family: var(--font-monospace); + font-size: 11.5px; + line-height: 1.5; +} + +.logfire-timeline li { + padding: 2px 10px; + color: var(--text-normal); + border-left: 2px solid var(--background-modifier-border); + margin-left: 6px; + transition: border-color 80ms ease; +} + +.logfire-timeline li:hover { + border-left-color: var(--interactive-accent); + background: color-mix(in srgb, var(--background-secondary) 40%, transparent); +} + +/* --------------------------------------------------------------------------- + Summary — Key-Value Readout + --------------------------------------------------------------------------- */ + +.logfire-summary { + font-family: var(--font-monospace); + font-size: 12px; + line-height: 1.6; + padding: 6px 0; +} + +.logfire-summary > div { + padding: 2px 10px; + border-bottom: 1px solid color-mix(in srgb, var(--background-modifier-border) 40%, transparent); +} + +.logfire-summary > div:last-child { + border-bottom: none; +} + +.logfire-summary strong { + color: var(--text-muted); + font-weight: 600; + letter-spacing: 0.01em; +} + +/* --------------------------------------------------------------------------- + Metric — Single Value Readout + --------------------------------------------------------------------------- */ + +.logfire-metric { + font-family: var(--font-monospace); + font-variant-numeric: tabular-nums; + color: var(--text-accent); + font-weight: 700; + letter-spacing: -0.02em; + padding: 8px 10px; + line-height: 1; +} + +/* --------------------------------------------------------------------------- + List + --------------------------------------------------------------------------- */ + +.logfire-list { + list-style: none; + padding: 0; + margin: 0; + font-family: var(--font-monospace); + font-size: 11.5px; + line-height: 1.5; +} + +.logfire-list li { + padding: 2px 10px; + color: var(--text-normal); + border-bottom: 1px solid color-mix(in srgb, var(--background-modifier-border) 40%, transparent); +} + +.logfire-list li:last-child { + border-bottom: none; +} + +.logfire-list li:hover { + background: color-mix(in srgb, var(--background-secondary) 40%, transparent); +} + +/* --------------------------------------------------------------------------- + Heatmap — Text-Based Bar Readout + --------------------------------------------------------------------------- */ + +.logfire-heatmap { + font-family: var(--font-monospace); + font-size: 11px; + line-height: 1.6; + padding: 4px 0; + font-variant-numeric: tabular-nums; +} + +.logfire-heatmap > div { + padding: 1px 10px; + color: var(--text-normal); + white-space: pre; + transition: background 60ms ease; +} + +.logfire-heatmap > div:hover { + background: color-mix(in srgb, var(--background-secondary) 40%, transparent); +} + +/* --------------------------------------------------------------------------- + Empty State & Error + --------------------------------------------------------------------------- */ + +.logfire-empty { + padding: 12px 10px; + color: var(--text-faint); + font-family: var(--font-monospace); + font-size: 11.5px; + font-style: italic; +} + +.logfire-error { + padding: 8px 10px; + margin: 0; + font-family: var(--font-monospace); + font-size: 11.5px; + line-height: 1.5; + color: var(--text-error, #e5534b); + background: color-mix(in srgb, var(--text-error, #e5534b) 8%, var(--background-primary)); + border-left: 3px solid var(--text-error, #e5534b); + border-radius: 0 4px 4px 0; + white-space: pre-wrap; + word-break: break-word; +}