Tabelle, Timeline, Summary, Metric, Liste, Heatmap sowie Markdown-Export und Wertformatierung (Timestamps, Zahlen). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
125 lines
4.5 KiB
TypeScript
125 lines
4.5 KiB
TypeScript
// ---------------------------------------------------------------------------
|
|
// 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);
|
|
}
|