mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-25 13:45:03 +08:00
f0e8ba1d8f
* feat: init session manger * feat: persist selected app to localStorage - Save app selection when switching providers - Restore last selected app on page load * feat: persist current view to localStorage - Save view selection when switching tabs - Restore last selected view on page load * styles: update ui * feat: Improve macOS Terminal activation and refactor Kitty launch to use command with user's default shell. * fix: session view * feat: toc * feat: Implement FlexSearch for improved session search functionality. * feat: Redesign session manager search and filter UI for a more compact and dynamic experience. * refactor: modularize session manager by extracting components and utility functions into dedicated files. * feat: Enhance session terminal launching with support for iTerm2, Ghostty, WezTerm, and Alacritty, including UI and custom configuration options. * feat: Conditionally render terminal selection and resume session buttons only on macOS.
78 lines
2.1 KiB
Rust
78 lines
2.1 KiB
Rust
use chrono::{DateTime, FixedOffset};
|
|
use serde_json::Value;
|
|
|
|
pub fn parse_timestamp_to_ms(value: &Value) -> Option<i64> {
|
|
let raw = value.as_str()?;
|
|
DateTime::parse_from_rfc3339(raw)
|
|
.ok()
|
|
.map(|dt: DateTime<FixedOffset>| dt.timestamp_millis())
|
|
}
|
|
|
|
pub fn extract_text(content: &Value) -> String {
|
|
match content {
|
|
Value::String(text) => text.to_string(),
|
|
Value::Array(items) => items
|
|
.iter()
|
|
.filter_map(|item| extract_text_from_item(item))
|
|
.filter(|text| !text.trim().is_empty())
|
|
.collect::<Vec<_>>()
|
|
.join("\n"),
|
|
Value::Object(map) => map
|
|
.get("text")
|
|
.and_then(|v| v.as_str())
|
|
.unwrap_or_default()
|
|
.to_string(),
|
|
_ => String::new(),
|
|
}
|
|
}
|
|
|
|
fn extract_text_from_item(item: &Value) -> Option<String> {
|
|
if let Some(text) = item.get("text").and_then(|v| v.as_str()) {
|
|
return Some(text.to_string());
|
|
}
|
|
|
|
if let Some(text) = item.get("input_text").and_then(|v| v.as_str()) {
|
|
return Some(text.to_string());
|
|
}
|
|
|
|
if let Some(text) = item.get("output_text").and_then(|v| v.as_str()) {
|
|
return Some(text.to_string());
|
|
}
|
|
|
|
if let Some(content) = item.get("content") {
|
|
let text = extract_text(content);
|
|
if !text.is_empty() {
|
|
return Some(text);
|
|
}
|
|
}
|
|
|
|
None
|
|
}
|
|
|
|
pub fn truncate_summary(text: &str, max_chars: usize) -> String {
|
|
let trimmed = text.trim();
|
|
if trimmed.is_empty() {
|
|
return String::new();
|
|
}
|
|
if trimmed.chars().count() <= max_chars {
|
|
return trimmed.to_string();
|
|
}
|
|
|
|
let mut result = trimmed.chars().take(max_chars).collect::<String>();
|
|
result.push_str("...");
|
|
result
|
|
}
|
|
|
|
pub fn path_basename(value: &str) -> Option<String> {
|
|
let trimmed = value.trim();
|
|
if trimmed.is_empty() {
|
|
return None;
|
|
}
|
|
let normalized = trimmed.trim_end_matches(|c| c == '/' || c == '\\');
|
|
let last = normalized
|
|
.split(['/', '\\'])
|
|
.last()
|
|
.filter(|segment| !segment.is_empty())?;
|
|
Some(last.to_string())
|
|
}
|