PranitInitial commit439f83c3 months agoHistory
134 lines · 5.5 KB · Zig
RawDownload
const std = @import("std");const session_display_metadata = @import("session_display_metadata.zig");const session_store = @import("session_store.zig");const text_utils = @import("../shared/text_utils.zig"); pub const Scope = enum {    current_workspace,    all_workspaces,}; pub const LoadState = enum {    loading,    ready,    failed,}; pub const ResumeFailure = enum {    open_elsewhere,    being_updated,    unavailable,}; pub fn filteredCount(summaries: []const session_store.SessionSummary, query: []const u8) usize {    var count: usize = 0;    for (summaries) |summary| {        if (matchesQuery(summary, query)) count += 1;    }    return count;} pub fn summaryAt(    summaries: []const session_store.SessionSummary,    query: []const u8,    display_index: usize,) ?*const session_store.SessionSummary {    var current: usize = 0;    for (summaries) |*summary| {        if (!matchesQuery(summary.*, query)) continue;        if (current == display_index) return summary;        current += 1;    }    return null;} pub fn displayTitle(summary: session_store.SessionSummary) []const u8 {    return summary.title orelse session_display_metadata.fallback_title;} pub fn workspacePath(summary: session_store.SessionSummary) []const u8 {    return summary.workspace_root orelse summary.origin_workspace_root orelse "(unknown workspace)";} pub fn relativeActivityAge(buf: []u8, updated_at_ms: i64, now_ms: i64) []const u8 {    const delta_ms: i64 = @max(0, now_ms - updated_at_ms);    if (delta_ms < std.time.ms_per_min) return "just now";    if (delta_ms < std.time.ms_per_hour) {        return std.fmt.bufPrint(buf, "{d}m ago", .{@divFloor(delta_ms, std.time.ms_per_min)}) catch "recently";    }    if (delta_ms < std.time.ms_per_day) {        return std.fmt.bufPrint(buf, "{d}h ago", .{@divFloor(delta_ms, std.time.ms_per_hour)}) catch "recently";    }    return std.fmt.bufPrint(buf, "{d}d ago", .{@divFloor(delta_ms, std.time.ms_per_day)}) catch "recently";} // Same buckets as relativeActivityAge without the " ago" suffix, for tight// single-line layouts: "now", "5m", "2h", "1d".pub fn relativeActivityAgeCompact(buf: []u8, updated_at_ms: i64, now_ms: i64) []const u8 {    const delta_ms: i64 = @max(0, now_ms - updated_at_ms);    if (delta_ms < std.time.ms_per_min) return "now";    if (delta_ms < std.time.ms_per_hour) {        return std.fmt.bufPrint(buf, "{d}m", .{@divFloor(delta_ms, std.time.ms_per_min)}) catch "recently";    }    if (delta_ms < std.time.ms_per_day) {        return std.fmt.bufPrint(buf, "{d}h", .{@divFloor(delta_ms, std.time.ms_per_hour)}) catch "recently";    }    return std.fmt.bufPrint(buf, "{d}d", .{@divFloor(delta_ms, std.time.ms_per_day)}) catch "recently";} fn matchesQuery(summary: session_store.SessionSummary, query: []const u8) bool {    const query_text = std.mem.trim(u8, query, " \t\r\n");    if (query_text.len == 0) return true;    if (text_utils.containsIgnoreCase(displayTitle(summary), query_text)) return true;    if (text_utils.containsIgnoreCase(workspacePath(summary), query_text)) return true;    if (summary.preview) |preview| {        if (text_utils.containsIgnoreCase(preview, query_text)) return true;    }    return false;} test "session catalog filters titles previews and workspace paths" {    const summaries = [_]session_store.SessionSummary{        .{            .id = @constCast("one"),            .workspace_root = @constCast("/worktrees/alpha"),            .title = @constCast("Fix renderer"),            .preview = @constCast("inspect footer state"),            .created_at_ms = 1,            .updated_at_ms = 2,            .conversation_language = .literal("en"),            .history_len = 4,        },        .{            .id = @constCast("two"),            .workspace_root = @constCast("/worktrees/beta"),            .title = @constCast("Review sessions"),            .created_at_ms = 1,            .updated_at_ms = 3,            .conversation_language = .literal("en"),            .history_len = 2,        },    };     try std.testing.expectEqual(@as(usize, 2), filteredCount(&summaries, ""));    try std.testing.expectEqual(@as(usize, 1), filteredCount(&summaries, "RENDER"));    try std.testing.expectEqualStrings("two", summaryAt(&summaries, "beta", 0).?.id);    try std.testing.expectEqualStrings("one", summaryAt(&summaries, "footer", 0).?.id);    try std.testing.expect(summaryAt(&summaries, "missing", 0) == null);} test "session catalog activity age uses the last update" {    var buf: [32]u8 = undefined;    try std.testing.expectEqualStrings("just now", relativeActivityAge(&buf, 1_000, 1_000));    try std.testing.expectEqualStrings("3m ago", relativeActivityAge(&buf, 1_000, 1_000 + 3 * std.time.ms_per_min));    try std.testing.expectEqualStrings("2h ago", relativeActivityAge(&buf, 1_000, 1_000 + 2 * std.time.ms_per_hour));    try std.testing.expectEqualStrings("3d ago", relativeActivityAge(&buf, 1_000, 1_000 + 3 * std.time.ms_per_day));} test "session catalog compact activity age drops the ago suffix" {    var buf: [32]u8 = undefined;    try std.testing.expectEqualStrings("now", relativeActivityAgeCompact(&buf, 1_000, 1_000));    try std.testing.expectEqualStrings("3m", relativeActivityAgeCompact(&buf, 1_000, 1_000 + 3 * std.time.ms_per_min));    try std.testing.expectEqualStrings("2h", relativeActivityAgeCompact(&buf, 1_000, 1_000 + 2 * std.time.ms_per_hour));    try std.testing.expectEqualStrings("3d", relativeActivityAgeCompact(&buf, 1_000, 1_000 + 3 * std.time.ms_per_day));}