diff --git a/src/main.rs b/src/main.rs index ffa5d0c..6f760be 100644 --- a/src/main.rs +++ b/src/main.rs @@ -32,6 +32,14 @@ struct Cli { #[arg(long)] filter: Option, + /// Only include sessions created on or after this date (YYYY-MM-DD) + #[arg(long)] + since: Option, + + /// Only include sessions created before this date (YYYY-MM-DD) + #[arg(long)] + before: Option, + /// Number of parallel worker threads (defaults to number of CPU cores) #[arg(short, long)] workers: Option, @@ -1687,6 +1695,7 @@ fn render_session(session: &Session, source_path: Option<&Path>) -> String { {CSS} +

{title}

@@ -2414,6 +2423,113 @@ footer { color: var(--text-muted); } +/* ----- Dark mode ----- */ +@media (prefers-color-scheme: dark) { + :root:not([data-theme="light"]) { + --bg: #111827; + --card-bg: #1f2937; + --text: #f3f4f6; + --text-muted: #9ca3af; + --border: #374151; + + --user-bg: #1e3a5f; + --user-border: #3b82f6; + --user-label: #93c5fd; + + --assistant-bg: #1f2937; + --assistant-border: #9ca3af; + --assistant-label: #d1d5db; + + --system-bg: #422006; + --system-border: #f59e0b; + --system-label: #fcd34d; + + --tool-result-bg: #064e3b; + --tool-result-border: #10b981; + --tool-result-label: #6ee7b7; + + --thinking-bg: #422006; + --thinking-border: #eab308; + --thinking-label: #fde68a; + + --tool-call-bg: #2e1065; + --tool-call-border: #a78bfa; + + --code-bg: #0f172a; + --code-text: #e2e8f0; + --code-border: #1e293b; + + --inline-code-bg: #374151; + --inline-code-text: #e5e7eb; + + --diff-add-bg: #1a2e1a; + --diff-add-text: #7ee787; + --diff-remove-bg: #2e1a1a; + --diff-remove-text: #f47067; + } +} + +[data-theme="dark"] { + --bg: #111827; + --card-bg: #1f2937; + --text: #f3f4f6; + --text-muted: #9ca3af; + --border: #374151; + + --user-bg: #1e3a5f; + --user-border: #3b82f6; + --user-label: #93c5fd; + + --assistant-bg: #1f2937; + --assistant-border: #9ca3af; + --assistant-label: #d1d5db; + + --system-bg: #422006; + --system-border: #f59e0b; + --system-label: #fcd34d; + + --tool-result-bg: #064e3b; + --tool-result-border: #10b981; + --tool-result-label: #6ee7b7; + + --thinking-bg: #422006; + --thinking-border: #eab308; + --thinking-label: #fde68a; + + --tool-call-bg: #2e1065; + --tool-call-border: #a78bfa; + + --code-bg: #0f172a; + --code-text: #e2e8f0; + --code-border: #1e293b; + + --inline-code-bg: #374151; + --inline-code-text: #e5e7eb; + + --diff-add-bg: #1a2e1a; + --diff-add-text: #7ee787; + --diff-remove-bg: #2e1a1a; + --diff-remove-text: #f47067; +} + +/* ----- Theme toggle button ----- */ +.theme-toggle { + position: fixed; + top: 12px; + right: 12px; + z-index: 100; + background: var(--card-bg); + border: 1px solid var(--border); + border-radius: 8px; + padding: 6px 10px; + cursor: pointer; + font-size: 1rem; + line-height: 1; + color: var(--text); + box-shadow: 0 1px 3px rgba(0,0,0,0.1); +} +.theme-toggle:hover { opacity: 0.8; } + /* ----- Responsive ----- */ @media (max-width: 600px) { .container { padding: 12px 8px; } @@ -2429,6 +2545,47 @@ footer { const JS: &str = r#" "##, CSS = CSS, @@ -3031,6 +3325,48 @@ mod tests { assert_eq!(extract_date_prefix("short"), None); } + // ----------------------------------------------------------------------- + // Date filter parsing tests + // ----------------------------------------------------------------------- + + #[test] + fn test_parse_date_filter() { + let d = parse_date_filter("2025-06-15"); + assert!(d.is_some()); + assert_eq!(d.unwrap().to_string(), "2025-06-15"); + + assert!(parse_date_filter("not-a-date").is_none()); + assert!(parse_date_filter("2025/06/15").is_none()); + } + + #[test] + fn test_parse_session_date_iso() { + let d = parse_session_date("2025-06-15T10:30:00+00:00"); + assert!(d.is_some()); + assert_eq!(d.unwrap().to_string(), "2025-06-15"); + } + + #[test] + fn test_parse_session_date_plain() { + let d = parse_session_date("2025-06-15"); + assert!(d.is_some()); + assert_eq!(d.unwrap().to_string(), "2025-06-15"); + } + + #[test] + fn test_parse_session_date_prefix() { + // Handles "2025-06-15T10:30:00Z" (non-standard but with T prefix) + let d = parse_session_date("2025-06-15T10:30:00Z"); + assert!(d.is_some()); + assert_eq!(d.unwrap().to_string(), "2025-06-15"); + } + + #[test] + fn test_parse_session_date_invalid() { + assert!(parse_session_date("not-a-date").is_none()); + assert!(parse_session_date("").is_none()); + } + // ----------------------------------------------------------------------- // Percent decode tests // -----------------------------------------------------------------------