feat(tui): show @handle in AI title and user input from oauth/token
This commit is contained in:
38
src/tui.rs
38
src/tui.rs
@@ -41,6 +41,8 @@ pub struct App {
|
||||
ai_status: String,
|
||||
ai_scroll: u16,
|
||||
ai_phase: AiPhase,
|
||||
ai_handle: String,
|
||||
user_handle: String,
|
||||
|
||||
agents: Vec<Agent>,
|
||||
selected: usize,
|
||||
@@ -72,6 +74,8 @@ impl App {
|
||||
let claude = ClaudeManager::spawn().ok();
|
||||
let ai_status = if claude.is_some() { "starting..." } else { "not available" };
|
||||
|
||||
let (user_handle, ai_handle) = load_handles();
|
||||
|
||||
let mut app = Self {
|
||||
claude,
|
||||
ai_output: String::new(),
|
||||
@@ -79,6 +83,8 @@ impl App {
|
||||
ai_status: ai_status.to_string(),
|
||||
ai_scroll: 0,
|
||||
ai_phase: AiPhase::Idle,
|
||||
ai_handle,
|
||||
user_handle,
|
||||
agents: Vec::new(),
|
||||
selected: 0,
|
||||
next_id: 1,
|
||||
@@ -663,6 +669,30 @@ fn parse_agent_commands(text: &str) -> Vec<(String, String, String)> {
|
||||
}
|
||||
|
||||
/// Load identity context from atproto config + recent chat.
|
||||
/// Load user and AI handles. Priority: oauth > token/bot > config.json
|
||||
fn load_handles() -> (String, String) {
|
||||
let cfg_dir = crate::config::config_dir();
|
||||
let base = format!("{cfg_dir}/ai.syui.log");
|
||||
|
||||
let read_handle = |path: &str| -> Option<String> {
|
||||
let content = std::fs::read_to_string(path).ok()?;
|
||||
let v: serde_json::Value = serde_json::from_str(&content).ok()?;
|
||||
v["handle"].as_str().map(|s| s.to_string())
|
||||
};
|
||||
|
||||
// User: oauth_session > token
|
||||
let user = read_handle(&format!("{base}/oauth_session.json"))
|
||||
.or_else(|| read_handle(&format!("{base}/token.json")))
|
||||
.unwrap_or_default();
|
||||
|
||||
// Bot: oauth_bot_session > bot
|
||||
let bot = read_handle(&format!("{base}/oauth_bot_session.json"))
|
||||
.or_else(|| read_handle(&format!("{base}/bot.json")))
|
||||
.unwrap_or_default();
|
||||
|
||||
(user, bot)
|
||||
}
|
||||
|
||||
fn load_identity_context() -> String {
|
||||
let config_path = crate::config::config_path();
|
||||
|
||||
@@ -800,10 +830,12 @@ fn render_ai_section(frame: &mut Frame, app: &App, area: Rect) {
|
||||
let border_style = Style::default().fg(ai_color);
|
||||
|
||||
let (ai_icon, ai_label_color) = ai_status_icon(app.ai_status.as_str());
|
||||
let ai_name = if app.ai_handle.is_empty() { "AI".to_string() }
|
||||
else { format!("@{}", app.ai_handle) };
|
||||
let title_spans = vec![
|
||||
Span::raw(" "),
|
||||
Span::styled(format!("{ai_icon} "), Style::default().fg(ai_label_color)),
|
||||
Span::styled("AI", Style::default().fg(ai_color).add_modifier(Modifier::BOLD)),
|
||||
Span::styled(&ai_name, Style::default().fg(ai_color).add_modifier(Modifier::BOLD)),
|
||||
Span::styled(format!(" {} ", app.ai_status), Style::default().fg(ai_label_color)),
|
||||
];
|
||||
|
||||
@@ -829,8 +861,10 @@ fn render_ai_section(frame: &mut Frame, app: &App, area: Rect) {
|
||||
.scroll((scroll, 0));
|
||||
frame.render_widget(output, layout[0]);
|
||||
|
||||
let user_label = if app.user_handle.is_empty() { ">".to_string() }
|
||||
else { format!("@{}", app.user_handle) };
|
||||
let input_line = Line::from(vec![
|
||||
Span::styled(" > ", Style::default().fg(ai_color)),
|
||||
Span::styled(format!(" {user_label} "), Style::default().fg(Color::DarkGray)),
|
||||
Span::raw(&app.ai_input),
|
||||
if ai_focused { Span::styled("_", Style::default().fg(ai_color)) }
|
||||
else { Span::raw("") },
|
||||
|
||||
Reference in New Issue
Block a user