feat: flag vendored clones and make the repo filter navigable

Two Repos-tab quality-of-life changes:

- External/vendored clones (repos with zero of your own commits — e.g. an
  upstream cloned for reference and never pushed to) are measured on
  refresh, cached in stats_cache (RepoStatsEntry.authored), shown with a
  dim ⧉ marker prefixed to the name, and hidden by the persistent
  hide_external_clones config toggle (key `x`, the `S` menu, or
  `vulp list --external|--no-external`). A fork you do commit to stays
  yours, so only true reference clones are flagged.

- Inside the `/` filter box, ↑↓ now move through the live-filtered
  results and Enter opens the highlighted repo/project directly, instead
  of the filter only "locking" on Enter with no way to scroll or select.
This commit is contained in:
dualfroz committed 2026-07-08 16:15:02 +02:00
1 parent 2ccf69f64c
commit 212c55d138
16 files changed
+120 -10

No files matched your search

+8
View File
@@ -62,6 +62,11 @@ pub struct AppConfig {
/// paths past these two roots are identical). Empty = feature disabled.
#[serde(default)]
pub deploy_root: String,
/// Hide vendored/reference clones (repos with zero of your own commits,
/// classified on `[R]`) from the Repos list. A durable preference rather
/// than a transient filter, so it lives here and not in the session file.
#[serde(default)]
pub hide_external_clones: bool,
}
fn default_license_kind() -> String { "mit".into() }
@@ -104,6 +109,7 @@ impl Default for AppConfig {
stats_author_emails: vec![],
deploy_ssh_cmd: String::new(),
deploy_root: String::new(),
hide_external_clones: false,
}
}
}
@@ -133,6 +139,7 @@ impl AppConfig {
("stats_author_emails", self.stats_author_emails.join(", ")),
("deploy_ssh_cmd", self.deploy_ssh_cmd.clone()),
("deploy_root", self.deploy_root.clone()),
("hide_external_clones", self.hide_external_clones.to_string()),
]
}
@@ -172,6 +179,7 @@ impl AppConfig {
}
"deploy_ssh_cmd" => self.deploy_ssh_cmd = value,
"deploy_root" => self.deploy_root = value,
"hide_external_clones" => self.hide_external_clones = value == "true",
"workspace_groups" => {
self.workspace_groups = value
.split(',')