Skip to content

Commit c53c98c

Browse files
committed
feat: vault SSH role picker, SSH disconnect toasts and UX improvements
Closes #26
1 parent 05f4e70 commit c53c98c

21 files changed

Lines changed: 408 additions & 90 deletions

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
## 2.36.0
2+
3+
- Vault SSH role picker and smarter defaults
4+
- Vault SSH Role field offers a picker when roles are already configured on other hosts or providers. Press Enter to pick or type one manually ([#26](https://github.com/erickochen/purple/issues/26))
5+
- Scheme-aware default ports for Vault address: `:443` for https, `:80` for http, `:8200` for bare hostnames
6+
- Vault SSH placeholder hints explain authentication flow ("auth via vault login")
7+
- SSH disconnect reasons shown in toast instead of just "exited with code 255". Full stderr context joined with pipe separators so you see why the connection was closed
8+
- Vault batch signing completion status now properly clears the sticky progress footer
9+
- Tags and alias placeholder hints clarified to avoid comma confusion
10+
- Tag input bar shows placeholder with cursor before hint text when empty
11+
- Updated dependencies to latest compatible versions
12+
113
## 2.35.1
214

315
- Internal cleanup. Leaner App struct and event loop

Cargo.lock

Lines changed: 44 additions & 38 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "purple-ssh"
3-
version = "2.35.1"
3+
version = "2.36.0"
44
edition = "2024"
55
description = "Open-source terminal SSH manager and SSH config editor. Search hundreds of hosts, sync from 16 clouds, transfer files, manage Docker and Podman over SSH, sign short-lived Vault SSH certs and expose an MCP server for AI agents. Rust TUI, MIT licensed."
66
license = "MIT"

llms.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ A: Press m in the host list to open the theme picker with live preview. 11 built
404404

405405
## Status
406406

407-
- Current version: 2.35.1 (April 2026)
407+
- Current version: 2.36.0 (April 2026)
408408
- Release cadence: approximately bi-weekly
409409
- Test suite: 6000+ tests (unit, integration, property-based and HTTP mocking)
410410
- CI: fmt, clippy, test on macOS and Linux, cargo-deny, MSRV 1.86 check

site/page.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
"url": "https://getpurple.sh",
4242
"downloadUrl": "https://getpurple.sh",
4343
"installUrl": "https://github.com/erickochen/purple/releases",
44-
"softwareVersion": "2.35.1",
44+
"softwareVersion": "2.36.0",
4545
"datePublished": "2024-10-01",
4646
"dateModified": "2026-04-13",
4747
"softwareRequirements": "macOS or Linux",
@@ -778,7 +778,7 @@ <h2 class="tagline">Open-source terminal SSH manager and SSH config editor for m
778778
<button class="copy-btn copy-inline" id="copy-btn" onclick="copy(this)" style="display:none">copy</button>
779779
</div>
780780
<div class="install-output" id="install-output" style="display:none">
781-
<div>Downloading purple v2.35.1 for darwin-arm64...</div>
781+
<div>Downloading purple v2.36.0 for darwin-arm64...</div>
782782
<div>Installing to /usr/local/bin/purple... <span class="success">done.</span></div>
783783
</div>
784784
<div class="alt-installs" id="alt-installs" style="display:none">
@@ -896,7 +896,7 @@ <h2 class="tagline">Open-source terminal SSH manager and SSH config editor for m
896896
<summary>How do I troubleshoot connection problems?</summary>
897897
<div class="answer">Run with <code>--verbose</code> to enable debug logging, then <code>purple logs --tail</code> in another terminal. Logs are written to <code>~/.purple/purple.log</code> with fault domain prefixes: <code>[external]</code> for remote/tool errors, <code>[config]</code> for local config issues. Set <code>PURPLE_LOG=trace</code> for maximum detail.</div>
898898
</details>
899-
<div class="man-foot"><span>purple v2.35.1</span><span>2026-04-13</span><span>PURPLE(1)</span></div>
899+
<div class="man-foot"><span>purple v2.36.0</span><span>2026-04-13</span><span>PURPLE(1)</span></div>
900900
</div>
901901
</div>
902902

site/worker.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ const LANDING_PAGE = `<!DOCTYPE html>
177177
"url": "https://getpurple.sh",
178178
"downloadUrl": "https://getpurple.sh",
179179
"installUrl": "https://github.com/erickochen/purple/releases",
180-
"softwareVersion": "2.35.1",
180+
"softwareVersion": "2.36.0",
181181
"datePublished": "2024-10-01",
182182
"dateModified": "2026-04-13",
183183
"softwareRequirements": "macOS or Linux",
@@ -914,7 +914,7 @@ footer .sep { margin: 0 0.3em; }
914914
<button class="copy-btn copy-inline" id="copy-btn" onclick="copy(this)" style="display:none">copy</button>
915915
</div>
916916
<div class="install-output" id="install-output" style="display:none">
917-
<div>Downloading purple v2.35.1 for darwin-arm64...</div>
917+
<div>Downloading purple v2.36.0 for darwin-arm64...</div>
918918
<div>Installing to /usr/local/bin/purple... <span class="success">done.</span></div>
919919
</div>
920920
<div class="alt-installs" id="alt-installs" style="display:none">
@@ -1032,7 +1032,7 @@ footer .sep { margin: 0 0.3em; }
10321032
<summary>How do I troubleshoot connection problems?</summary>
10331033
<div class="answer">Run with <code>--verbose</code> to enable debug logging, then <code>purple logs --tail</code> in another terminal. Logs are written to <code>~/.purple/purple.log</code> with fault domain prefixes: <code>[external]</code> for remote/tool errors, <code>[config]</code> for local config issues. Set <code>PURPLE_LOG=trace</code> for maximum detail.</div>
10341034
</details>
1035-
<div class="man-foot"><span>purple v2.35.1</span><span>2026-04-13</span><span>PURPLE(1)</span></div>
1035+
<div class="man-foot"><span>purple v2.36.0</span><span>2026-04-13</span><span>PURPLE(1)</span></div>
10361036
</div>
10371037
</div>
10381038
@@ -1493,7 +1493,7 @@ A: Press m in the host list to open the theme picker with live preview. 11 built
14931493
14941494
## Status
14951495
1496-
- Current version: 2.35.1 (April 2026)
1496+
- Current version: 2.36.0 (April 2026)
14971497
- Release cadence: approximately bi-weekly
14981498
- Test suite: 6000+ tests (unit, integration, property-based and HTTP mocking)
14991499
- CI: fmt, clippy, test on macOS and Linux, cargo-deny, MSRV 1.86 check

src/app.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,8 @@ impl App {
258258
password_picker_state: ListState::default(),
259259
show_proxyjump_picker: false,
260260
proxyjump_picker_state: ListState::default(),
261+
show_vault_role_picker: false,
262+
vault_role_picker_state: ListState::default(),
261263
tag_picker_state: ListState::default(),
262264
theme_picker_state: ListState::default(),
263265
theme_picker_builtins: Vec::new(),

src/app/selection.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,40 @@ impl App {
280280
super::cycle_selection(&mut self.ui.proxyjump_picker_state, len, true);
281281
}
282282

283+
/// Collect unique Vault SSH roles from all hosts and providers, sorted.
284+
pub fn vault_role_candidates(&self) -> Vec<String> {
285+
let mut seen = std::collections::HashSet::new();
286+
let mut roles = Vec::new();
287+
for host in &self.hosts {
288+
if let Some(ref role) = host.vault_ssh {
289+
if seen.insert(role.clone()) {
290+
roles.push(role.clone());
291+
}
292+
}
293+
}
294+
// Also collect from provider configs.
295+
for section in &self.provider_config.sections {
296+
let role = section.vault_role.trim();
297+
if !role.is_empty() && seen.insert(role.to_string()) {
298+
roles.push(role.to_string());
299+
}
300+
}
301+
roles.sort();
302+
roles
303+
}
304+
305+
/// Move vault role picker selection up.
306+
pub fn select_prev_vault_role(&mut self) {
307+
let len = self.vault_role_candidates().len();
308+
super::cycle_selection(&mut self.ui.vault_role_picker_state, len, false);
309+
}
310+
311+
/// Move vault role picker selection down.
312+
pub fn select_next_vault_role(&mut self) {
313+
let len = self.vault_role_candidates().len();
314+
super::cycle_selection(&mut self.ui.vault_role_picker_state, len, true);
315+
}
316+
283317
/// Collect all unique tags from hosts, sorted alphabetically.
284318
pub fn collect_unique_tags(&self) -> Vec<String> {
285319
let mut seen = std::collections::HashSet::new();

0 commit comments

Comments
 (0)