From aba060f3a5faf823ed9c2eb491fc719c2a53174c Mon Sep 17 00:00:00 2001 From: tolvitty Date: Thu, 12 Feb 2026 11:17:22 +0100 Subject: [PATCH] Processor: Chart-Support und Dashboard-Block-Prozessor logfire-sql Bloecke unterstuetzen jetzt -- chart: Direktiven fuer Inline-Visualisierung. Neuer logfire-dashboard Block- Prozessor rendert Multi-Widget-Dashboards direkt in Notizen. Co-Authored-By: Claude Opus 4.6 --- src/query/processor.ts | 80 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 2 deletions(-) diff --git a/src/query/processor.ts b/src/query/processor.ts index 2395931..df73888 100644 --- a/src/query/processor.ts +++ b/src/query/processor.ts @@ -3,6 +3,9 @@ import { QueryConfig, TimeRange, EventType, EventCategory } from '../types'; import { buildQuery } from '../core/query-builder'; import { DatabaseManager } from '../core/database'; import { renderTable, renderTimeline, renderSummary, renderMetric, renderList, renderHeatmap, formatValue } from '../viz/table-renderer'; +import { renderChart, parseChartConfig } from '../viz/chart-renderer'; +import { parseDashboardBlock, DashboardView, DASHBOARD_VIEW_TYPE } from '../viz/dashboard'; +import type LogfirePlugin from '../main'; // --------------------------------------------------------------------------- // Refresh timer management @@ -84,13 +87,20 @@ export function registerLogfireSqlBlock( return; } + const chartConfig = parseChartConfig(source); + try { const rows = db.queryReadOnly(sql) as Record[]; if (!Array.isArray(rows) || rows.length === 0) { el.createEl('p', { text: 'Keine Ergebnisse.', cls: 'logfire-empty' }); return; } - renderTable(el, rows); + + if (chartConfig) { + renderChart(el, rows, chartConfig); + } else { + renderTable(el, rows); + } if (refresh && refresh > 0) { setupRefreshTimer(el, () => { @@ -101,7 +111,11 @@ export function registerLogfireSqlBlock( el.createEl('p', { text: 'Keine Ergebnisse.', cls: 'logfire-empty' }); return; } - renderTable(el, freshRows); + if (chartConfig) { + renderChart(el, freshRows, chartConfig); + } else { + renderTable(el, freshRows); + } } catch (err) { renderError(el, err); } @@ -113,6 +127,68 @@ export function registerLogfireSqlBlock( }); } +// --------------------------------------------------------------------------- +// `logfire-dashboard` block — Dashboard Code-Block +// --------------------------------------------------------------------------- + +export function registerLogfireDashboardBlock( + plugin: LogfirePlugin, + db: DatabaseManager, + registerFn: (language: string, handler: (source: string, el: HTMLElement, ctx: MarkdownPostProcessorContext) => void) => void, +): void { + registerFn('logfire-dashboard', (source, el, ctx) => { + const dashboard = parseDashboardBlock(source); + if (!dashboard) { + renderError(el, new Error('Ungültige Dashboard-Definition.')); + return; + } + + // Render dashboard inline + const wrapper = el.createDiv({ cls: 'logfire-dash-inline' }); + + if (dashboard.name) { + wrapper.createDiv({ cls: 'logfire-dash-inline-title', text: dashboard.name }); + } + + const grid = wrapper.createDiv({ cls: 'logfire-dash-grid' }); + grid.style.gridTemplateColumns = `repeat(${dashboard.columns}, 1fr)`; + + for (const widget of dashboard.widgets) { + const widgetEl = grid.createDiv({ cls: 'logfire-dash-widget' }); + widgetEl.style.gridColumn = `${widget.position.col + 1} / span ${widget.position.width}`; + widgetEl.style.gridRow = `${widget.position.row + 1} / span ${widget.position.height}`; + + if (widget.title) { + widgetEl.createDiv({ cls: 'logfire-dash-widget-title', text: widget.title }); + } + + const content = widgetEl.createDiv({ cls: 'logfire-dash-widget-content' }); + + try { + if (widget.type === 'text' && widget.text) { + content.createDiv({ text: widget.text }); + } else if (widget.sql) { + const rows = db.queryReadOnly(widget.sql) as Record[]; + if (rows.length === 0) { + content.createDiv({ cls: 'logfire-empty', text: 'Keine Ergebnisse.' }); + } else if (widget.type === 'chart' && widget.chartConfig) { + renderChart(content, rows, widget.chartConfig); + } else if (widget.type === 'stat') { + renderMetric(content, rows); + } else { + renderTable(content, rows); + } + } + } catch (err) { + content.createDiv({ + cls: 'logfire-error', + text: `Fehler: ${err instanceof Error ? err.message : String(err)}`, + }); + } + } + }); +} + // --------------------------------------------------------------------------- // YAML config parsing // ---------------------------------------------------------------------------