Translates all German user-facing strings (command names, notices, settings, modal labels, template names/descriptions, error messages, status bar, and code comments) to English. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
150 lines
4.8 KiB
TypeScript
150 lines
4.8 KiB
TypeScript
import { ItemView, WorkspaceLeaf } from 'obsidian';
|
|
import { LogfireEvent, EventCategory } from '../types';
|
|
import { EventBus } from '../core/event-bus';
|
|
|
|
export const EVENT_STREAM_VIEW_TYPE = 'logfire-event-stream';
|
|
const MAX_VISIBLE_ENTRIES = 500;
|
|
|
|
export class EventStreamView extends ItemView {
|
|
private entries: LogfireEvent[] = [];
|
|
private unsubscribe: (() => void) | null = null;
|
|
private listEl!: HTMLElement;
|
|
private viewPaused = false;
|
|
private filterText = '';
|
|
private enabledCategories = new Set<EventCategory>([
|
|
'file', 'content', 'navigation', 'editor', 'vault', 'plugin', 'system',
|
|
]);
|
|
|
|
constructor(leaf: WorkspaceLeaf, private eventBus: EventBus) {
|
|
super(leaf);
|
|
}
|
|
|
|
getViewType(): string {
|
|
return EVENT_STREAM_VIEW_TYPE;
|
|
}
|
|
|
|
getDisplayText(): string {
|
|
return 'Logfire Events';
|
|
}
|
|
|
|
getIcon(): string {
|
|
return 'activity';
|
|
}
|
|
|
|
async onOpen(): Promise<void> {
|
|
const container = this.containerEl.children[1] as HTMLElement;
|
|
container.empty();
|
|
container.addClass('logfire-event-stream');
|
|
|
|
// Toolbar
|
|
const toolbar = container.createDiv({ cls: 'logfire-stream-toolbar' });
|
|
|
|
const searchInput = toolbar.createEl('input', {
|
|
type: 'text',
|
|
placeholder: 'Filter by source...',
|
|
cls: 'logfire-stream-search',
|
|
});
|
|
searchInput.addEventListener('input', () => {
|
|
this.filterText = searchInput.value.toLowerCase();
|
|
this.renderList();
|
|
});
|
|
|
|
const btnGroup = toolbar.createDiv({ cls: 'logfire-stream-btn-group' });
|
|
|
|
const pauseBtn = btnGroup.createEl('button', { text: 'Pause' });
|
|
pauseBtn.addEventListener('click', () => {
|
|
this.viewPaused = !this.viewPaused;
|
|
pauseBtn.textContent = this.viewPaused ? 'Resume' : 'Pause';
|
|
pauseBtn.toggleClass('is-active', this.viewPaused);
|
|
});
|
|
|
|
const clearBtn = btnGroup.createEl('button', { text: 'Clear' });
|
|
clearBtn.addEventListener('click', () => {
|
|
this.entries = [];
|
|
this.renderList();
|
|
});
|
|
|
|
// Category toggles
|
|
const categoryBar = container.createDiv({ cls: 'logfire-stream-categories' });
|
|
|
|
const categories: EventCategory[] = ['file', 'content', 'navigation', 'editor', 'vault', 'plugin', 'system'];
|
|
for (const cat of categories) {
|
|
const label = categoryBar.createEl('label', { cls: 'logfire-cat-toggle' });
|
|
const cb = label.createEl('input', { type: 'checkbox' }) as HTMLInputElement;
|
|
cb.checked = true;
|
|
label.createSpan({ text: cat });
|
|
cb.addEventListener('change', () => {
|
|
if (cb.checked) {
|
|
this.enabledCategories.add(cat);
|
|
} else {
|
|
this.enabledCategories.delete(cat);
|
|
}
|
|
this.renderList();
|
|
});
|
|
}
|
|
|
|
// Event list
|
|
this.listEl = container.createDiv({ cls: 'logfire-stream-list' });
|
|
|
|
// Subscribe to events
|
|
this.unsubscribe = this.eventBus.onEvent('*', (event) => {
|
|
if (this.viewPaused) return;
|
|
this.entries.unshift(event);
|
|
if (this.entries.length > MAX_VISIBLE_ENTRIES) {
|
|
this.entries.length = MAX_VISIBLE_ENTRIES;
|
|
}
|
|
this.renderList();
|
|
});
|
|
}
|
|
|
|
async onClose(): Promise<void> {
|
|
this.unsubscribe?.();
|
|
this.unsubscribe = null;
|
|
}
|
|
|
|
private renderList(): void {
|
|
this.listEl.empty();
|
|
|
|
const filtered = this.entries.filter(e => {
|
|
if (!this.enabledCategories.has(e.category)) return false;
|
|
if (this.filterText && !e.source.toLowerCase().includes(this.filterText)) return false;
|
|
return true;
|
|
});
|
|
|
|
for (const event of filtered.slice(0, MAX_VISIBLE_ENTRIES)) {
|
|
const row = this.listEl.createDiv({ cls: 'logfire-stream-row' });
|
|
|
|
const time = new Date(event.timestamp);
|
|
const timeStr = `${String(time.getHours()).padStart(2, '0')}:${String(time.getMinutes()).padStart(2, '0')}:${String(time.getSeconds()).padStart(2, '0')}`;
|
|
|
|
const payloadSummary = this.summarizePayload(event);
|
|
|
|
row.createSpan({ text: timeStr, cls: 'logfire-stream-time' });
|
|
row.createSpan({ text: event.type, cls: 'logfire-stream-type' });
|
|
row.createSpan({ text: event.source || '', cls: 'logfire-stream-source' });
|
|
if (payloadSummary) {
|
|
row.createSpan({ text: payloadSummary, cls: 'logfire-stream-payload' });
|
|
}
|
|
}
|
|
}
|
|
|
|
private summarizePayload(event: LogfireEvent): string {
|
|
const p = event.payload;
|
|
switch (event.type) {
|
|
case 'content:words-changed':
|
|
return `+${p.wordsAdded ?? 0}/-${p.wordsRemoved ?? 0} words`;
|
|
case 'editor:change':
|
|
return `+${p.insertedChars ?? 0}/-${p.deletedChars ?? 0} chars`;
|
|
case 'nav:file-close':
|
|
return typeof p.duration === 'number' ? `${Math.round(p.duration / 1000)}s` : '';
|
|
case 'file:rename':
|
|
return `→ ${p.newName ?? ''}`;
|
|
case 'file:move':
|
|
return `→ ${p.newFolder ?? ''}`;
|
|
case 'plugin:command-executed':
|
|
return String(p.commandName ?? '');
|
|
default:
|
|
return '';
|
|
}
|
|
}
|
|
}
|