fix: route warnings to file and add in-app toast notifications

- Replace all eprintln! with tracing::warn! to avoid TUI corruption
  via stderr writes during alternate screen mode
- Route tracing output to ~/.local/share/zesdex/zesdex.log instead of
  stderr by configuring tracing_subscriber with a Mutex<File> writer
- Add render_toasts() widget: floating notification stack at top-right
  of the terminal, color-coded by severity (Info/Success/Warning/Error/
  Lesson), auto-expires after 5s, max 4 visible
- Apply to 12 files: stream parser, MCP client, subagent engine,
  state/rest, controller/input, app_config, provider, OAuth, agent_def,
  dto/chat/tool

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
asepharyana
2026-07-12 10:57:32 +07:00
co-authored by Claude Opus 4.8
parent e29dfadaa7
commit 89b63b1bc2
13 changed files with 92 additions and 30 deletions
+6 -6
View File
@@ -70,23 +70,23 @@ impl SseParser {
let value: Value = match serde_json::from_str(&data) {
Ok(v) => v,
Err(e) => {
eprintln!("[stream] failed to parse chunk: {}", e);
tracing::warn!("[stream] failed to parse chunk: {}", e);
return vec![];
}
};
if let Some(usage) = value.get("usage") {
if !usage.is_null() {
let prompt_tokens = usage.get("prompt_tokens").and_then(|v| v.as_u64()).unwrap_or_else(|| {
eprintln!("[stream] prompt_tokens missing in usage chunk");
tracing::warn!("[stream] prompt_tokens missing in usage chunk");
0
});
let completion_tokens = usage.get("completion_tokens").and_then(|v| v.as_u64()).unwrap_or_else(|| {
eprintln!("[stream] completion_tokens missing in usage chunk");
tracing::warn!("[stream] completion_tokens missing in usage chunk");
0
});
let total_tokens = usage.get("total_tokens").and_then(|v| v.as_u64())
.unwrap_or_else(|| {
eprintln!("[stream] total_tokens missing in usage chunk");
tracing::warn!("[stream] total_tokens missing in usage chunk");
prompt_tokens + completion_tokens
});
return vec![StreamEvent::Usage { prompt_tokens, completion_tokens, total_tokens }];
@@ -125,7 +125,7 @@ impl SseParser {
let mut events = Vec::with_capacity(tool_calls.len());
for tc in tool_calls {
let index = tc.get("index").and_then(|i| i.as_u64()).unwrap_or_else(|| {
eprintln!("[stream] tool call delta missing index, defaulting to 0");
tracing::warn!("[stream] tool call delta missing index, defaulting to 0");
0
}) as usize;
let id = tc.get("id").and_then(|i| i.as_str()).map(|s| s.to_string());
@@ -202,7 +202,7 @@ pub fn parse_stream_chunk(data: &str) -> Option<StreamEvent> {
if let Some(tool_calls) = delta.get("tool_calls").and_then(|tc| tc.as_array()) {
if let Some(tc) = tool_calls.first() {
let index = tc.get("index").and_then(|i| i.as_u64()).unwrap_or_else(|| {
eprintln!("[stream] fallback parser: tool call missing index, defaulting to 0");
tracing::warn!("[stream] fallback parser: tool call missing index, defaulting to 0");
0
}) as usize;
let id = tc.get("id").and_then(|i| i.as_str()).map(|s| s.to_string());