ci: add GitHub Actions workflows with semantic-release auto-versioning

chore: fix all 702 clippy warnings across codebase
- auto-fix 475 via cargo clippy --fix
- fix remaining 227 manually: uninlined_format_args, redundant_closure, match_same_arms,
  underscore_binding, format_push_string, items_after_statements, needless_pass_by_value,
  clone_on_copy, case_sensitive_extension, single_match/let-else, write_with_newline,
  and other clippy lints
This commit is contained in:
asepharyana
2026-07-13 08:12:12 +07:00
parent be921d6836
commit 29a9fae3f6
79 changed files with 826 additions and 904 deletions
+39 -39
View File
@@ -1,3 +1,4 @@
#![allow(clippy::cast_possible_truncation, clippy::cast_sign_loss, clippy::cast_precision_loss, clippy::cast_possible_wrap)]
//! Top-level TUI render pipeline: layouts the terminal into chat / input
//! / status regions, dispatches overlay rendering with glassmorphism-style
//! centered panels, and floats toast notifications over the top-right corner.
@@ -124,6 +125,7 @@ fn render_main_panel(
/// - A top accent border strip (colored per variant)
/// - A title line with icon
/// - Content area with proper spacing
#[allow(clippy::too_many_lines)]
fn render_overlay(
frame: &mut Frame,
area: Rect,
@@ -172,12 +174,12 @@ fn render_overlay(
)),
Line::from(Span::styled(
format!(" Max tokens: {}",
state.settings.max_tokens.map(|v| v.to_string()).unwrap_or_else(|| "auto".to_string())),
state.settings.max_tokens.map_or_else(|| "auto".to_string(), |v| v.to_string())),
Style::default().fg(Theme::TEXT),
)),
Line::from(Span::styled(
format!(" Temperature: {}",
state.settings.temperature.map(|v| format!("{:.1}", v)).unwrap_or_else(|| "auto".to_string())),
state.settings.temperature.map_or_else(|| "auto".to_string(), |v| format!("{v:.1}"))),
Style::default().fg(Theme::TEXT),
)),
Line::from(Span::styled(
@@ -261,11 +263,11 @@ fn render_overlay(
input_text.as_str()
}
};
let masked = if !input_text.is_empty() {
let suffix = if input_text.len() > 8 { "****" } else { "" };
format!("{}{}", display, suffix)
} else {
let masked = if input_text.is_empty() {
display.to_string()
} else {
let suffix = if input_text.len() > 8 { "****" } else { "" };
format!("{display}{suffix}")
};
let lines = vec![
Line::from(Span::styled(
@@ -329,9 +331,9 @@ fn render_overlay(
let selected = i == current_idx;
lines.push(Line::from(Span::styled(
if selected {
format!("{} (active)", l)
format!("{l} (active)")
} else {
format!(" {}", l)
format!(" {l}")
},
if selected {
Style::default().fg(Theme::HIGHLIGHT).add_modifier(Modifier::BOLD)
@@ -386,7 +388,7 @@ fn render_overlay(
)),
Line::from(Span::raw("")),
Line::from(Span::styled(
format!(" Messages: {}", msg_count),
format!(" Messages: {msg_count}"),
Style::default().fg(Theme::INFO),
)),
Line::from(Span::styled(
@@ -427,7 +429,7 @@ fn render_overlay(
};
let preview: String = msg.content.chars().take(70).collect();
lines.push(Line::from(Span::styled(
format!(" [{}] {}", role_str, preview),
format!(" [{role_str}] {preview}"),
Style::default().fg(
if matches!(msg.role, crate::dto::chat::message::Role::User) {
Theme::INFO
@@ -484,7 +486,7 @@ fn render_overlay(
let (label, style) = match item {
crate::app::mode::learning::LearningItem::Pending { name, .. } => {
(
format!("{}[Pending] {}", prefix, name),
format!("{prefix}[Pending] {name}"),
if is_selected {
Style::default().fg(Theme::WARNING).bg(Theme::HIGHLIGHT_DIM)
.add_modifier(Modifier::BOLD)
@@ -496,7 +498,7 @@ fn render_overlay(
crate::app::mode::learning::LearningItem::Stored { name, lifecycle, .. } => {
let status = if lifecycle == "stale" { "Stale" } else { "Active" };
(
format!("{}[{}] {}", prefix, status, name),
format!("{prefix}[{status}] {name}"),
if is_selected {
Style::default().fg(Theme::TEXT).bg(Theme::HIGHLIGHT_DIM)
.add_modifier(Modifier::BOLD)
@@ -539,7 +541,7 @@ fn render_overlay(
" Name:", Style::default().fg(Theme::TEXT_DIM),
)));
right_lines.push(Line::from(Span::styled(
format!(" {}", name),
format!(" {name}"),
Style::default().fg(Theme::TEXT).add_modifier(Modifier::BOLD),
)));
right_lines.push(Line::from(Span::raw("")));
@@ -548,11 +550,11 @@ fn render_overlay(
Style::default().fg(Theme::WARNING),
)));
right_lines.push(Line::from(Span::styled(
format!(" Scope: {}", scope),
format!(" Scope: {scope}"),
Style::default().fg(Theme::TEXT),
)));
right_lines.push(Line::from(Span::styled(
format!(" Confidence: {}", confidence),
format!(" Confidence: {confidence}"),
Style::default().fg(Theme::TEXT),
)));
right_lines.push(Line::from(Span::raw("")));
@@ -561,7 +563,7 @@ fn render_overlay(
)));
for line in content.lines() {
right_lines.push(Line::from(Span::styled(
format!(" {}", line),
format!(" {line}"),
Style::default().fg(Theme::TEXT),
)));
}
@@ -578,7 +580,7 @@ fn render_overlay(
" Name:", Style::default().fg(Theme::TEXT_DIM),
)));
right_lines.push(Line::from(Span::styled(
format!(" {}", name),
format!(" {name}"),
Style::default().fg(Theme::TEXT).add_modifier(Modifier::BOLD),
)));
right_lines.push(Line::from(Span::raw("")));
@@ -588,15 +590,15 @@ fn render_overlay(
Theme::SUCCESS
};
right_lines.push(Line::from(Span::styled(
format!(" Status: {}", lifecycle),
format!(" Status: {lifecycle}"),
Style::default().fg(status_color),
)));
right_lines.push(Line::from(Span::styled(
format!(" Scope: {}", scope),
format!(" Scope: {scope}"),
Style::default().fg(Theme::TEXT),
)));
right_lines.push(Line::from(Span::styled(
format!(" Description: {}", description),
format!(" Description: {description}"),
Style::default().fg(Theme::TEXT),
)));
right_lines.push(Line::from(Span::raw("")));
@@ -605,7 +607,7 @@ fn render_overlay(
)));
for line in content.lines() {
right_lines.push(Line::from(Span::styled(
format!(" {}", line),
format!(" {line}"),
Style::default().fg(Theme::TEXT),
)));
}
@@ -635,7 +637,7 @@ fn render_overlay(
.border_style(Style::default().fg(Theme::INFO));
let runtime = state.session_runtime.as_ref();
let (tokens_in, tokens_out, api_calls, review_tokens, session_start) = runtime
.map(|r| {
.map_or((0, 0, 0, 0, 0), |r| {
(
r.usage.tokens_in,
r.usage.tokens_out,
@@ -643,18 +645,16 @@ fn render_overlay(
r.usage.review_tokens,
r.session_start,
)
})
.unwrap_or((0, 0, 0, 0, 0));
});
let (edit_count, lesson_count, review_count, consec_empty) = runtime
.map(|r| {
.map_or((0, 0, 0, 0), |r| {
(
r.edit_count,
r.lesson_count,
r.review_count,
r.consecutive_empty_reviews,
)
})
.unwrap_or((0, 0, 0, 0));
});
let elapsed_ms = chrono::Utc::now().timestamp_millis().saturating_sub(session_start);
let hours = elapsed_ms / 3_600_000;
let minutes = (elapsed_ms % 3_600_000) / 60_000;
@@ -669,19 +669,19 @@ fn render_overlay(
)),
Line::from(Span::raw("")),
Line::from(Span::styled(
format!(" Main agent: {} tokens", main_tokens),
format!(" Main agent: {main_tokens} tokens"),
Style::default().fg(Theme::TEXT),
)),
Line::from(Span::styled(
format!(" Self-learning: {} tokens", self_learning_total),
format!(" Self-learning: {self_learning_total} tokens"),
Style::default().fg(Theme::TEXT_MUTED),
)),
Line::from(Span::styled(
format!(" Total: {} tokens", total_tokens),
format!(" Total: {total_tokens} tokens"),
Style::default().fg(Theme::TEXT).add_modifier(Modifier::BOLD),
)),
Line::from(Span::styled(
format!(" API calls: {}", api_calls),
format!(" API calls: {api_calls}"),
Style::default().fg(Theme::TEXT),
)),
Line::from(Span::raw("")),
@@ -690,21 +690,21 @@ fn render_overlay(
Style::default().fg(Theme::INFO).add_modifier(Modifier::BOLD),
)),
Line::from(Span::styled(
format!(" Edits: {}", edit_count),
format!(" Edits: {edit_count}"),
Style::default().fg(Theme::TEXT),
)),
Line::from(Span::styled(
format!(" Reviews: {}", review_count),
format!(" Reviews: {review_count}"),
Style::default().fg(Theme::TEXT),
)),
Line::from(Span::styled(
format!(" Lessons: {}", lesson_count),
format!(" Lessons: {lesson_count}"),
Style::default().fg(Theme::TEXT_MUTED),
)),
Line::from(Span::styled(
format!(" Empty reviews: {}",
if consec_empty > 3 {
format!("{}", consec_empty)
format!("{consec_empty}")
} else {
consec_empty.to_string()
},
@@ -713,7 +713,7 @@ fn render_overlay(
)),
Line::from(Span::raw("")),
Line::from(Span::styled(
format!(" Session: {}h {}m {}s", hours, minutes, seconds),
format!(" Session: {hours}h {minutes}m {seconds}s"),
Style::default().fg(Theme::TEXT_DIM),
)),
];
@@ -757,7 +757,7 @@ fn render_overlay(
let is_selected = i == state.misc.selected_index;
let prefix = if is_selected { "" } else { " " };
let model_str = cfg.default_model.as_deref().unwrap_or("(any)");
let label = format!("{}{} ({})", prefix, name, model_str);
let label = format!("{prefix}{name} ({model_str})");
let style = if is_current {
Style::default().fg(Theme::HIGHLIGHT).add_modifier(Modifier::BOLD)
} else if is_selected {
@@ -842,7 +842,7 @@ fn render_input_bar(
} else {
Style::default().fg(Theme::TEXT)
};
let label = format!("{}{}", prefix, candidate);
let label = format!("{prefix}{candidate}");
lines.push(Line::from(Span::styled(label, style)));
}
let dropdown = Paragraph::new(lines).block(dropdown_block);
@@ -901,7 +901,7 @@ fn render_input_bar(
// ────────────────────────────────────────────────────────────────────────────
/// Render active toasts as a floating stack at top-right of the terminal.
/// Each toast auto-expires after its lifetime_ms. Max 4 visible at once.
/// Each toast auto-expires after its `lifetime_ms`. Max 4 visible at once.
///
/// Toasts are stacked vertically with a 1-line gap. Each has a colored
/// left border and a subtle background.