Source code for continuity.utils.pager
"""
Pager utilities for enhanced content viewing.
Provides smart pager detection and beautiful content formatting.
"""
import os
import shutil
import subprocess
import tempfile
from rich.console import Console
console = Console()
[docs]
def detect_pager() -> str:
"""Detect the best available pager.
Returns:
Pager command string
"""
# Check PAGER environment variable first
pager = os.environ.get("PAGER")
if pager and shutil.which(pager.split()[0]):
return pager
# Check for enhanced pagers
enhanced_pagers = ["bat", "batcat", "less", "more"]
for pager in enhanced_pagers:
if shutil.which(pager):
# Add useful flags for each pager
if pager in ["bat", "batcat"]:
return f"{pager} --style=header,grid --theme=ansi"
elif pager == "less":
return "less -R" # -R for ANSI color support
else:
return pager
# Fallback to more (should be available on most systems)
return "more"
[docs]
def format_message_content(
message, show_metadata: bool = True, user_context=None
) -> str:
"""Format message content for display.
Args:
message: Message object with content, metadata
show_metadata: Whether to include metadata header
user_context: UserContext for nickname mapping (optional)
Returns:
Formatted content string
"""
content_parts = []
if show_metadata:
# Apply nickname mapping if user_context is provided
sender_name = message.sender
recipient_name = message.recipient
if user_context:
sender_name = user_context.get_display_name(message.sender)
recipient_name = user_context.get_display_name(message.recipient)
# Create metadata header
metadata_lines = [
f"From: {sender_name}",
f"To: {recipient_name}",
f"Date: {message.timestamp.strftime('%Y-%m-%d %H:%M:%S')}",
f"Title: {message.title or 'Untitled'}",
f"Status: {'Read' if message.read else 'Unread'}",
f"ID: {message.id}",
]
# Use a simple text-based header for better pager compatibility
header = "=" * 60 + "\n"
header += "\n".join(metadata_lines)
header += "\n" + "=" * 60 + "\n\n"
content_parts.append(header)
# Add the message content
content_parts.append(message.content)
# Add footer if metadata is shown
if show_metadata:
footer = "\n\n" + "-" * 60 + "\n"
footer += "Navigation: q=quit, space=next page, b=back, /=search"
content_parts.append(footer)
return "".join(content_parts)
[docs]
def format_thread_content(thread_name: str, posts: list) -> str:
"""Format thread content for display.
Args:
thread_name: Name of the thread
posts: List of post objects
Returns:
Formatted thread content
"""
content_parts = []
# Thread header
header = "=" * 60 + "\n"
header += f"Thread: {thread_name}\n"
header += f"Posts: {len(posts)}\n"
header += "=" * 60 + "\n\n"
content_parts.append(header)
# Format each post
for i, post in enumerate(posts, 1):
post_header = f"--- Post {i} ---\n"
post_header += f"Author: {post.get('author', 'Unknown')}\n"
post_header += f"Date: {post.get('timestamp', 'Unknown')}\n"
post_header += "-" * 40 + "\n\n"
content_parts.append(post_header)
content_parts.append(post["content"])
content_parts.append("\n\n")
# Footer
footer = "=" * 60 + "\n"
footer += "End of thread\n"
footer += "Navigation: q=quit, space=next page, b=back, /=search"
content_parts.append(footer)
return "".join(content_parts)
[docs]
def view_with_pager(content: str, title: str = "Content") -> bool:
"""Display content using system pager.
Args:
content: Content to display
title: Title for the content (used in temp filename)
Returns:
True if content was displayed successfully
"""
pager_cmd = detect_pager()
# Create temporary file with the content
try:
with tempfile.NamedTemporaryFile(
mode="w",
suffix=".txt",
prefix=f'continuity-{title.lower().replace(" ", "-")}-',
delete=False,
encoding="utf-8",
) as tmp_file:
tmp_file.write(content)
tmp_path = tmp_file.name
# Run pager command
try:
pager_parts = pager_cmd.split()
result = subprocess.run(pager_parts + [tmp_path], check=True)
return True
except subprocess.CalledProcessError:
# Fallback to console print if pager fails
console.print("[yellow]Pager failed, displaying in console:[/yellow]")
console.print(content)
return True
except FileNotFoundError:
# Pager not found, fallback to console
console.print(
f"[yellow]Pager '{pager_cmd}' not found, displaying in console:[/yellow]"
)
console.print(content)
return True
except Exception as e:
console.print(f"[red]Error displaying content:[/red] {e}")
return False
finally:
# Clean up temporary file
try:
if "tmp_path" in locals():
os.unlink(tmp_path)
except OSError:
pass
[docs]
def preview_content(content: str, max_lines: int = 10) -> str:
"""Create a preview of content for quick display.
Args:
content: Full content
max_lines: Maximum lines to show in preview
Returns:
Preview content with ellipsis if truncated
"""
lines = content.split("\n")
if len(lines) <= max_lines:
return content
preview_lines = lines[:max_lines]
preview_lines.append(f"... ({len(lines) - max_lines} more lines)")
return "\n".join(preview_lines)