Table-Renderer: Render-Funktionen fuer Query-Ergebnisse
Tabelle, Timeline, Summary, Metric, Liste, Heatmap sowie Markdown-Export und Wertformatierung (Timestamps, Zahlen). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
00446c4227
commit
b2fc5b8f6b
1 changed files with 125 additions and 0 deletions
125
src/viz/table-renderer.ts
Normal file
125
src/viz/table-renderer.ts
Normal file
|
|
@ -0,0 +1,125 @@
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Table
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export function renderTable(el: HTMLElement, rows: Record<string, unknown>[], columns?: string[]): void {
|
||||||
|
const table = el.createEl('table', { cls: 'logfire-table' });
|
||||||
|
const allKeys = columns ?? Object.keys(rows[0]);
|
||||||
|
|
||||||
|
const thead = table.createEl('thead');
|
||||||
|
const headerRow = thead.createEl('tr');
|
||||||
|
for (const key of allKeys) {
|
||||||
|
headerRow.createEl('th', { text: key });
|
||||||
|
}
|
||||||
|
|
||||||
|
const tbody = table.createEl('tbody');
|
||||||
|
for (const row of rows) {
|
||||||
|
const tr = tbody.createEl('tr');
|
||||||
|
for (const key of allKeys) {
|
||||||
|
tr.createEl('td', { text: formatValue(row[key]) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Timeline
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export function renderTimeline(el: HTMLElement, rows: Record<string, unknown>[]): void {
|
||||||
|
const list = el.createEl('ul', { cls: 'logfire-timeline' });
|
||||||
|
for (const row of rows) {
|
||||||
|
const ts = typeof row.timestamp === 'number'
|
||||||
|
? new Date(row.timestamp).toLocaleTimeString()
|
||||||
|
: '';
|
||||||
|
const type = String(row.type ?? '');
|
||||||
|
const source = String(row.source ?? '');
|
||||||
|
list.createEl('li', { text: `${ts} ${type} ${source}` });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Summary
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export function renderSummary(el: HTMLElement, rows: Record<string, unknown>[]): void {
|
||||||
|
const container = el.createDiv({ cls: 'logfire-summary' });
|
||||||
|
for (const row of rows) {
|
||||||
|
for (const [key, value] of Object.entries(row)) {
|
||||||
|
const line = container.createDiv();
|
||||||
|
line.createEl('strong', { text: `${key}: ` });
|
||||||
|
line.appendText(formatValue(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Metric (single big number)
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export function renderMetric(el: HTMLElement, rows: Record<string, unknown>[]): void {
|
||||||
|
const record = rows[0];
|
||||||
|
const values = Object.values(record);
|
||||||
|
const value = values.length > 0 ? values[values.length - 1] : 0;
|
||||||
|
const div = el.createEl('div', {
|
||||||
|
text: formatValue(value),
|
||||||
|
cls: 'logfire-metric',
|
||||||
|
});
|
||||||
|
div.style.fontSize = '2em';
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// List
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export function renderList(el: HTMLElement, rows: Record<string, unknown>[]): void {
|
||||||
|
const list = el.createEl('ul', { cls: 'logfire-list' });
|
||||||
|
for (const row of rows) {
|
||||||
|
const text = Object.values(row).map(formatValue).join(' | ');
|
||||||
|
list.createEl('li', { text });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Heatmap (text-based bar chart)
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export function renderHeatmap(el: HTMLElement, rows: Record<string, unknown>[]): void {
|
||||||
|
const container = el.createDiv({ cls: 'logfire-heatmap' });
|
||||||
|
|
||||||
|
for (const row of rows) {
|
||||||
|
const group = String(row.group ?? '');
|
||||||
|
const count = Number(row.count ?? 0);
|
||||||
|
const bar = '\u2588'.repeat(Math.min(count, 50));
|
||||||
|
container.createDiv({ text: `${group} ${bar} ${count}` });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Markdown table generation (for copy/export)
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export function toMarkdownTable(keys: string[], rows: Record<string, unknown>[]): string {
|
||||||
|
const header = `| ${keys.join(' | ')} |`;
|
||||||
|
const separator = `| ${keys.map(() => '---').join(' | ')} |`;
|
||||||
|
const body = rows.map(row =>
|
||||||
|
`| ${keys.map(k => {
|
||||||
|
const v = row[k];
|
||||||
|
return v === null || v === undefined ? '' : String(v);
|
||||||
|
}).join(' | ')} |`
|
||||||
|
).join('\n');
|
||||||
|
|
||||||
|
return `${header}\n${separator}\n${body}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Value formatting
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export function formatValue(value: unknown): string {
|
||||||
|
if (value === null || value === undefined) return '';
|
||||||
|
if (typeof value === 'number') {
|
||||||
|
if (value > 1e12) return new Date(value).toLocaleString();
|
||||||
|
return value.toLocaleString();
|
||||||
|
}
|
||||||
|
return String(value);
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue