@@ -400,40 +400,67 @@ def compose(self):
400400 )
401401
402402
403- class LogEntry (Horizontal ):
404- """A single log entry displayed as a table row with 4 columns ."""
403+ class LogEntry (Vertical ):
404+ """A single log entry that can be expanded to show details ."""
405405
406406 def __init__ (self , logger_name : str , level : str , message : str , timestamp : str = "" , ** kwargs ):
407407 super ().__init__ (** kwargs )
408408 self .logger_name = logger_name
409409 self .level = level
410410 self .message = message
411411 self .timestamp = timestamp
412+ self .is_expanded = False
412413 self .classes = f"log-entry log-{ level .lower ()} "
413414
414415 def compose (self ):
415- # Column 1: Offset time with brackets (just the time portion, not full timestamp)
416- time_part = self .timestamp .split ()[- 1 ] if self .timestamp else ""
417- yield Static (f"[{ time_part } ]" , classes = "log-col-time" )
418-
419- # Column 2: Level
420- yield Static (self .level , classes = "log-col-level" )
421-
422- # Column 3: Location (logger name) - truncate if longer than 20 characters
423- location = self .logger_name
424- if len (location ) > 9 :
425- location = location [:9 ] + "..."
426- yield Static (location , classes = "log-col-location" )
427-
428- # Column 4: Message with green checkmark for successful outcomes
429- message_text = self .message
430- # Add green checkmark for messages indicating success
431- if any (
432- keyword in message_text .lower ()
433- for keyword in ["completed" , "success" , "successfully" , "passed" , "done" , "✓" ]
434- ):
435- message_text = f"[green]✓[/green] { message_text } "
436- yield Static (message_text , classes = "log-col-message" )
416+ # Main row: just the message with a clickable indicator
417+ with Horizontal (classes = "log-main-row" ):
418+ # Expandable indicator
419+ yield Static ("▶" , classes = "log-expand-indicator" )
420+
421+ time_part = self .timestamp .split ()[- 1 ] if self .timestamp else ""
422+ time_prefix = f"[#888888][{ time_part } ][/#888888] " if time_part else ""
423+ indent_spaces = len (f"[{ time_part } ] " ) if time_part else 0
424+
425+ message_body = self .message
426+ if any (
427+ keyword in self .message .lower ()
428+ for keyword in ["completed" , "success" , "successfully" , "passed" , "done" , "✓" ]
429+ ):
430+ message_body = f"[green]✓[/green] { message_body } "
431+
432+ if indent_spaces and "\n " in message_body :
433+ message_body = message_body .replace ("\n " , "\n " + " " * indent_spaces )
434+
435+ yield Static (f"{ time_prefix } { message_body } " , classes = "log-col-message" )
436+
437+ # Details row (hidden by default) - vertical layout
438+ with Vertical (id = f"log-details-{ id (self )} " , classes = "log-details-row" ):
439+ location = self .logger_name
440+ if len (location ) > 20 :
441+ location = location [:20 ] + "..."
442+ yield Static (f" [#888]level:[/#888] { self .level } " , classes = "log-details-text" )
443+ yield Static (f" [#888]location:[/#888] { location } " , classes = "log-details-text" )
444+
445+ def on_mount (self ) -> None :
446+ """Hide details on mount."""
447+ try :
448+ details = self .query_one (f"#log-details-{ id (self )} " )
449+ details .display = False
450+ except Exception :
451+ pass
452+
453+ def on_click (self ) -> None :
454+ """Toggle details visibility on click."""
455+ try :
456+ details = self .query_one (f"#log-details-{ id (self )} " )
457+ indicator = self .query_one (".log-expand-indicator" , Static )
458+
459+ self .is_expanded = not self .is_expanded
460+ details .display = self .is_expanded
461+ indicator .update ("▼" if self .is_expanded else "▶" )
462+ except Exception :
463+ pass
437464
438465
439466class StructuredLogView (VerticalScroll ):
0 commit comments