Add UI modules and tests
Some checks failed
CI / test (push) Has been cancelled

This commit is contained in:
2026-01-30 09:11:31 +00:00
parent fce47dab09
commit e09a6a63fa

110
src/ui/timeline.py Normal file
View File

@@ -0,0 +1,110 @@
"""Timeline view for DevTrace events."""
from datetime import datetime
from typing import List, Optional, Dict, Any
from rich.console import Console
from rich.table import Table
from rich.panel import Panel
from ..storage.models import (
FileEvent,
CommandEvent,
GitEvent,
TimelineEvent
)
class TimelineView:
"""Displays development events in a timeline format."""
EVENT_ICONS = {
"created": "📄",
"modified": "✏️",
"deleted": "🗑️",
"moved": "📁",
"commit": "💾",
"branch": "🌿",
"merge": "🔀",
"checkout": "🔀",
"push": "📤",
"pull": "📥",
}
def __init__(self, events: Optional[List] = None):
self.events = events or []
self.console = Console()
def _format_timestamp(self, timestamp: Optional[datetime]) -> str:
"""Format timestamp for display."""
if timestamp is None:
return "N/A"
now = datetime.now()
delta = now - timestamp
if delta.total_seconds() < 60:
return "Just now"
elif delta.total_seconds() < 3600:
mins = int(delta.total_seconds() / 60)
return f"{mins}m ago"
elif delta.total_seconds() < 86400:
hours = int(delta.total_seconds() / 3600)
return f"{hours}h ago"
else:
return timestamp.strftime("%Y-%m-%d %H:%M")
def _get_event_color(self, event_type: str) -> str:
"""Get color for event type."""
colors = {
"file": "cyan",
"command": "green",
"git": "magenta",
}
return colors.get(event_type, "white")
def display(self, limit: int = 50) -> None:
"""Display the timeline."""
if not self.events:
self.console.print("[bold yellow]No events to display[/]")
return
table = Table(title="Timeline", show_header=True, header_style="bold magenta")
table.add_column("Time", width=12, style="cyan")
table.add_column("Icon", width=4, justify="center")
table.add_column("Type", width=10)
table.add_column("Details", overflow="fold")
for event in self.events[:limit]:
if isinstance(event, FileEvent):
icon = self.EVENT_ICONS.get(event.event_type, "📄")
details = event.file_path
elif isinstance(event, CommandEvent):
icon = ""
details = event.short_command
elif isinstance(event, GitEvent):
icon = self.EVENT_ICONS.get(event.operation, "🔧")
details = f"{event.operation} {event.branch or event.short_hash}"
else:
icon = "📌"
details = str(event)
color = self._get_event_color(type(event).__name__.replace("Event", "").lower())
time_str = self._format_timestamp(getattr(event, 'timestamp', None))
table.add_row(
f"[{color}]{time_str}[/]",
icon,
f"[{color}]{type(event).__name__.replace('Event', '')}[/]",
details
)
self.console.print(table)
def get_event_counts(self) -> Dict[str, int]:
"""Get counts of each event type."""
counts = {}
for event in self.events:
event_type = type(event).__name__.replace("Event", "").lower()
counts[event_type] = counts.get(event_type, 0) + 1
return counts