clipmem 0.4.2

macOS clipboard memory backed by SQLite and searchable from agent runtimes
Documentation
use super::*;

pub(in crate::cli) fn render_capture_once_text(output: &CaptureOnceOutput) -> String {
    let mut out = String::new();
    match output {
        CaptureOnceOutput::Stored(output) => {
            let store_state = if output.store.inserted_new_snapshot() {
                "new content"
            } else {
                "already known content"
            };
            let _ = writeln!(
                out,
                "stored snapshot {} via event {} ({store_state})",
                output.store.snapshot_id(),
                output.store.event_id()
            );
            let _ = writeln!(out, "kind: {}", output.snapshot.snapshot_kind());
            let _ = writeln!(out, "bytes: {}", output.snapshot.total_bytes());
            if let Some(app) = output.snapshot.frontmost_app_name() {
                let _ = writeln!(out, "frontmost app: {app}");
            }
            if !output.snapshot.preview_text().is_empty() {
                let _ = writeln!(out, "preview: {}", output.snapshot.preview_text());
            }
        }
        CaptureOnceOutput::Skipped(output) => {
            let _ = writeln!(out, "skipped capture reason={}", output.reason.as_str());
            let _ = writeln!(out, "kind: {}", output.kind);
            let _ = writeln!(out, "bytes: {}", output.total_bytes);
            if let Some(app) = &output.frontmost_app_name {
                let _ = writeln!(out, "frontmost app: {app}");
            }
        }
    }

    out
}

pub(in crate::cli) fn render_hits_text(hits: &[SearchHit]) -> String {
    let mut out = String::new();
    for hit in hits {
        let app = hit
            .last_frontmost_app_name()
            .or(hit.last_frontmost_app_bundle_id())
            .unwrap_or("unknown app");

        let _ = writeln!(
            out,
            "[{}] {} · last seen {} · captures {} · {} · {} bytes · {} items",
            hit.snapshot_id(),
            hit.snapshot_kind(),
            format_utc_timestamp(hit.last_observed_at()),
            hit.capture_count(),
            app,
            hit.total_bytes(),
            hit.item_count()
        );

        if !hit.preview_text().is_empty() {
            let _ = writeln!(out, "  preview: {}", hit.preview_text());
        }
        if let Some(why_matched) = hit
            .why_matched()
            .filter(|value| *value != hit.preview_text())
        {
            let _ = writeln!(out, "  match:   {why_matched}");
        }
        if !hit.urls().is_empty() {
            let _ = writeln!(out, "  urls:    {}", hit.urls().join(", "));
        }
        if !hit.file_paths().is_empty() {
            let _ = writeln!(out, "  files:   {}", hit.file_paths().join(", "));
        }
        if let Some(score) = hit.score() {
            let _ = writeln!(out, "  score:   {score:.3}");
        }
        push_blank_line(&mut out);
    }
    out
}

pub(in crate::cli) fn render_timeline_text(events: &[TimelineEvent]) -> String {
    let mut out = String::new();
    for event in events {
        let app = event
            .frontmost_app_name()
            .or(event.frontmost_app_bundle_id())
            .unwrap_or("unknown app");

        let _ = writeln!(
            out,
            "event {} · snapshot {} · {} · change_count={} · {} · {} bytes",
            event.event_id(),
            event.snapshot_id(),
            format_utc_timestamp(event.observed_at()),
            event.change_count(),
            app,
            event.total_bytes()
        );

        if !event.best_text().is_empty() {
            let _ = writeln!(out, "  best:    {}", event.best_text());
        }
        if !event.preview_text().is_empty() && event.preview_text() != event.best_text() {
            let _ = writeln!(out, "  preview: {}", event.preview_text());
        }
        if !event.urls().is_empty() {
            let _ = writeln!(out, "  urls:    {}", event.urls().join(", "));
        }
        if !event.file_paths().is_empty() {
            let _ = writeln!(out, "  files:   {}", event.file_paths().join(", "));
        }
        push_blank_line(&mut out);
    }
    out
}

pub(in crate::cli) fn render_search_results_text(results: &SearchResults) -> String {
    let mut out = String::new();
    let _ = writeln!(out, "search mode: {}", results.mode_used().as_str());
    if !results.hits().is_empty() {
        out.push('\n');
    }
    out.push_str(&render_hits_text(results.hits()));
    out
}

pub(in crate::cli) fn render_stats_text(stats: &StatsReport) -> String {
    let mut out = String::new();
    let _ = writeln!(out, "Overview");
    let _ = writeln!(out, "  snapshots: {}", stats.snapshot_count);
    let _ = writeln!(out, "  capture events: {}", stats.capture_event_count);
    let _ = writeln!(out, "  unique apps: {}", stats.unique_app_count);
    let _ = writeln!(out, "  total bytes: {}", stats.total_bytes);
    let _ = writeln!(
        out,
        "  average bytes per snapshot: {:.1}",
        stats.average_bytes_per_snapshot
    );
    let _ = writeln!(
        out,
        "  average captures per snapshot: {:.2}",
        stats.average_captures_per_snapshot
    );
    let _ = writeln!(out, "  dedupe ratio: {:.1}%", stats.dedupe_ratio * 100.0);
    let _ = writeln!(
        out,
        "  observed: {} to {}",
        stats
            .first_observed_at
            .as_deref()
            .map_or("none".to_string(), format_utc_timestamp),
        stats
            .last_observed_at
            .as_deref()
            .map_or("none".to_string(), format_utc_timestamp)
    );
    let _ = writeln!(
        out,
        "  archive span: {}",
        stats.archive_span_seconds.map_or_else(
            || "0s".to_string(),
            |seconds| { format_duration_seconds(seconds.max(0) as u64) }
        )
    );
    push_blank_line(&mut out);

    let _ = writeln!(out, "Content mix");
    if stats.kind_breakdown.is_empty() {
        let _ = writeln!(out, "  none");
    } else {
        for entry in &stats.kind_breakdown {
            let _ = writeln!(
                out,
                "  {}: {} snapshots, {} bytes",
                entry.kind, entry.snapshot_count, entry.total_bytes
            );
        }
    }
    push_blank_line(&mut out);

    let _ = writeln!(out, "Top apps");
    if stats.top_apps.is_empty() {
        let _ = writeln!(out, "  none");
    } else {
        for app in &stats.top_apps {
            let _ = writeln!(out, "  {}: {} events", app.app, app.capture_event_count);
        }
    }
    push_blank_line(&mut out);

    let _ = writeln!(out, "Activity patterns");
    let busiest_hour = peak_bucket(&stats.busiest_hours).map_or_else(
        || "none".to_string(),
        |entry| {
            format!(
                "{}:00 UTC ({} events)",
                entry.bucket, entry.capture_event_count
            )
        },
    );
    let busiest_weekday = peak_bucket(&stats.busiest_weekdays).map_or_else(
        || "none".to_string(),
        |entry| format!("{} ({} events)", entry.bucket, entry.capture_event_count),
    );
    let _ = writeln!(out, "  Busiest hour: {busiest_hour}");
    let _ = writeln!(out, "  Busiest weekday: {busiest_weekday}");
    push_blank_line(&mut out);

    let _ = writeln!(out, "Leaderboards");
    if let Some(snapshot) = &stats.most_recopied_snapshot {
        let _ = writeln!(out, "  Most re-copied snapshot:");
        push_snapshot_leaderboard_entry(&mut out, snapshot, 4);
    } else {
        let _ = writeln!(out, "  Most re-copied snapshot: none");
    }
    let _ = writeln!(out, "  Largest snapshots:");
    push_snapshot_leaderboard(&mut out, &stats.largest_snapshots, 4);
    let _ = writeln!(out, "  Most captured snapshots:");
    push_snapshot_leaderboard(&mut out, &stats.most_captured_snapshots, 4);

    out
}

pub(in crate::cli) fn render_snapshot_text(snapshot: &SnapshotDetails) -> String {
    let mut out = String::new();
    let _ = writeln!(
        out,
        "snapshot {} · sha256={} · kind={} · captures={}",
        snapshot.snapshot_id(),
        snapshot.sha256(),
        snapshot.snapshot_kind(),
        snapshot.capture_count()
    );
    let _ = writeln!(
        out,
        "first seen: {} · last seen: {}",
        format_utc_timestamp(snapshot.first_observed_at()),
        format_utc_timestamp(snapshot.last_observed_at())
    );
    let _ = writeln!(
        out,
        "bytes: {} · items: {}",
        snapshot.total_bytes(),
        snapshot.item_count()
    );
    if let Some(app) = snapshot
        .last_frontmost_app_name()
        .or(snapshot.last_frontmost_app_bundle_id())
    {
        let _ = writeln!(out, "last frontmost app: {app}");
    }
    if !snapshot.best_text().is_empty() {
        let _ = writeln!(out, "best: {}", snapshot.best_text());
    }
    if !snapshot.preview_text().is_empty() {
        let _ = writeln!(out, "preview: {}", snapshot.preview_text());
    }
    if !snapshot.urls().is_empty() {
        let _ = writeln!(out, "urls: {}", snapshot.urls().join(", "));
    }
    if !snapshot.file_paths().is_empty() {
        let _ = writeln!(out, "files: {}", snapshot.file_paths().join(", "));
    }
    if let Some(ocr_text) = snapshot.ocr_text() {
        let _ = writeln!(out, "ocr: {ocr_text}");
    }
    if let Some(ocr_status) = snapshot.ocr_status() {
        let _ = writeln!(out, "ocr status: {ocr_status}");
    }
    push_blank_line(&mut out);

    for item in snapshot.items() {
        let _ = writeln!(
            out,
            "item {} · kind={} · bytes={}{}",
            item.item_index(),
            item.primary_kind(),
            item.total_bytes(),
            item.primary_uti()
                .map(|uti| format!(" · primary type={uti}"))
                .unwrap_or_default()
        );
        if !item.preview_text().is_empty() {
            let _ = writeln!(out, "  preview: {}", item.preview_text());
        }
        for rep in item.representations() {
            let _ = writeln!(
                out,
                "  - {} · kind={} · {} bytes · sha256={}",
                rep.uti(),
                rep.kind(),
                rep.byte_len(),
                rep.raw_sha256()
            );
            if let Some(text) = rep.text_value() {
                let _ = writeln!(out, "    text: {text}");
            }
        }
        push_blank_line(&mut out);
    }

    if !snapshot.recent_events().is_empty() {
        let _ = writeln!(out, "recent events:");
        for event in snapshot.recent_events() {
            let source = event
                .frontmost_app_name()
                .or(event.frontmost_app_bundle_id())
                .unwrap_or("unknown app");
            let _ = writeln!(
                out,
                "  event {} · {} · change_count={} · {}",
                event.event_id(),
                format_utc_timestamp(event.observed_at()),
                event.change_count(),
                source
            );
        }
    }

    out
}

pub(in crate::cli) fn render_doctor_text(report: &DoctorReport) -> String {
    let mut out = String::new();
    let _ = writeln!(out, "database: {}", report.db_path());
    let _ = writeln!(out, "sqlite version: {}", report.sqlite_version());
    let _ = writeln!(out, "journal mode: {}", report.journal_mode());
    let _ = writeln!(
        out,
        "fts5 compile option present: {}",
        report.fts5_compile_option_present()
    );
    let _ = writeln!(
        out,
        "fts5 temp table creation works: {}",
        report.fts5_create_virtual_table_ok()
    );

    if !report.compile_options().is_empty() {
        out.push('\n');
        let _ = writeln!(out, "compile options:");
        for opt in report.compile_options() {
            let _ = writeln!(out, "  {opt}");
        }
    }

    out
}