Adjust login form x2

This commit is contained in:
Ultradesu
2026-06-10 16:35:39 +01:00
parent d16c7a0b03
commit e72bd1592e
4 changed files with 295 additions and 19 deletions
Generated
+250 -3
View File
@@ -60,6 +60,24 @@ dependencies = [
"num-traits",
]
[[package]]
name = "arboard"
version = "3.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf"
dependencies = [
"clipboard-win",
"log",
"objc2",
"objc2-app-kit",
"objc2-foundation",
"parking_lot",
"percent-encoding",
"windows-sys 0.60.2",
"wl-clipboard-rs",
"x11rb",
]
[[package]]
name = "arrayvec"
version = "0.7.6"
@@ -423,6 +441,15 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "clipboard-win"
version = "5.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4"
dependencies = [
"error-code",
]
[[package]]
name = "cmake"
version = "0.1.58"
@@ -863,6 +890,12 @@ dependencies = [
"litrs",
]
[[package]]
name = "downcast-rs"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
[[package]]
name = "dunce"
version = "1.0.5"
@@ -953,6 +986,12 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "error-code"
version = "3.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59"
[[package]]
name = "euclid"
version = "0.22.14"
@@ -1075,6 +1114,12 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "fixedbitset"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
[[package]]
name = "flate2"
version = "1.1.9"
@@ -1138,6 +1183,7 @@ name = "furumi_tui"
version = "0.1.0"
dependencies = [
"anyhow",
"arboard",
"core-foundation 0.10.1",
"crokey",
"crossterm",
@@ -1257,6 +1303,16 @@ dependencies = [
"version_check",
]
[[package]]
name = "gethostname"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8"
dependencies = [
"rustix 1.1.4",
"windows-link",
]
[[package]]
name = "getrandom"
version = "0.2.17"
@@ -2085,6 +2141,15 @@ dependencies = [
"minimal-lexical",
]
[[package]]
name = "nom"
version = "8.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405"
dependencies = [
"memchr",
]
[[package]]
name = "nu-ansi-term"
version = "0.50.3"
@@ -2199,6 +2264,18 @@ dependencies = [
"objc2-encode",
]
[[package]]
name = "objc2-app-kit"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c"
dependencies = [
"bitflags 2.13.0",
"objc2",
"objc2-core-graphics",
"objc2-foundation",
]
[[package]]
name = "objc2-audio-toolbox"
version = "0.3.2"
@@ -2260,6 +2337,19 @@ dependencies = [
"objc2",
]
[[package]]
name = "objc2-core-graphics"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807"
dependencies = [
"bitflags 2.13.0",
"dispatch2",
"objc2",
"objc2-core-foundation",
"objc2-io-surface",
]
[[package]]
name = "objc2-encode"
version = "4.1.0"
@@ -2279,6 +2369,17 @@ dependencies = [
"objc2-core-foundation",
]
[[package]]
name = "objc2-io-surface"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d"
dependencies = [
"bitflags 2.13.0",
"objc2",
"objc2-core-foundation",
]
[[package]]
name = "once_cell"
version = "1.21.4"
@@ -2327,6 +2428,16 @@ dependencies = [
"pin-project-lite",
]
[[package]]
name = "os_pipe"
version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967"
dependencies = [
"libc",
"windows-sys 0.61.2",
]
[[package]]
name = "palette"
version = "0.7.6"
@@ -2435,6 +2546,17 @@ dependencies = [
"sha2",
]
[[package]]
name = "petgraph"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455"
dependencies = [
"fixedbitset 0.5.7",
"hashbrown 0.15.5",
"indexmap",
]
[[package]]
name = "phf"
version = "0.11.3"
@@ -2639,6 +2761,15 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
[[package]]
name = "quick-xml"
version = "0.39.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdcc8dd4e2f670d309a5f0e83fe36dfdc05af317008fea29144da1a2ac858e5e"
dependencies = [
"memchr",
]
[[package]]
name = "quinn"
version = "0.11.9"
@@ -3026,7 +3157,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys 0.4.15",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -3683,7 +3814,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4ea810f0692f9f51b382fff5893887bb4580f5fa246fde546e0b13e7fcee662"
dependencies = [
"fnv",
"nom",
"nom 7.1.3",
"phf",
"phf_codegen",
]
@@ -3709,7 +3840,7 @@ dependencies = [
"fancy-regex",
"filedescriptor",
"finl_unicode",
"fixedbitset",
"fixedbitset 0.4.2",
"hex",
"lazy_static",
"libc",
@@ -4057,6 +4188,17 @@ dependencies = [
"tracing-log",
]
[[package]]
name = "tree_magic_mini"
version = "3.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8765b90061cba6c22b5831f675da109ae5561588290f9fa2317adab2714d5a6"
dependencies = [
"memchr",
"nom 8.0.0",
"petgraph",
]
[[package]]
name = "try-lock"
version = "0.2.5"
@@ -4335,6 +4477,76 @@ dependencies = [
"semver",
]
[[package]]
name = "wayland-backend"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2857dd20b54e916ec7253b3d6b4d5c4d7d4ca2c33c2e11c6c76a99bd8744755d"
dependencies = [
"cc",
"downcast-rs",
"rustix 1.1.4",
"smallvec",
"wayland-sys",
]
[[package]]
name = "wayland-client"
version = "0.31.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "645c7c96bb74690c3189b5c9cb4ca1627062bb23693a4fad9d8c3de958260144"
dependencies = [
"bitflags 2.13.0",
"rustix 1.1.4",
"wayland-backend",
"wayland-scanner",
]
[[package]]
name = "wayland-protocols"
version = "0.32.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "563a85523cade2429938e790815fd7319062103b9f4a2dc806e9b53b95982d8f"
dependencies = [
"bitflags 2.13.0",
"wayland-backend",
"wayland-client",
"wayland-scanner",
]
[[package]]
name = "wayland-protocols-wlr"
version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234"
dependencies = [
"bitflags 2.13.0",
"wayland-backend",
"wayland-client",
"wayland-protocols",
"wayland-scanner",
]
[[package]]
name = "wayland-scanner"
version = "0.31.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c324a910fd86ebdc364a3e61ec1f11737d3b1d6c273c0239ee8ff4bc0d24b4a"
dependencies = [
"proc-macro2",
"quick-xml",
"quote",
]
[[package]]
name = "wayland-sys"
version = "0.31.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be"
dependencies = [
"pkg-config",
]
[[package]]
name = "web-sys"
version = "0.3.100"
@@ -5001,12 +5213,47 @@ dependencies = [
"wasmparser",
]
[[package]]
name = "wl-clipboard-rs"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9651471a32e87d96ef3a127715382b2d11cc7c8bb9822ded8a7cc94072eb0a3"
dependencies = [
"libc",
"log",
"os_pipe",
"rustix 1.1.4",
"thiserror 2.0.18",
"tree_magic_mini",
"wayland-backend",
"wayland-client",
"wayland-protocols",
"wayland-protocols-wlr",
]
[[package]]
name = "writeable"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4"
[[package]]
name = "x11rb"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414"
dependencies = [
"gethostname",
"rustix 1.1.4",
"x11rb-protocol",
]
[[package]]
name = "x11rb-protocol"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd"
[[package]]
name = "xdg-home"
version = "1.3.0"
+1
View File
@@ -9,6 +9,7 @@ path = "src/main.rs"
[dependencies]
anyhow = "1.0.102"
arboard = { version = "3.6.1", default-features = false, features = ["wayland-data-control"] }
crokey = "1.4.0"
crossterm = { version = "0.29.0", features = ["event-stream"] }
directories = "6.0.0"
+29
View File
@@ -59,6 +59,31 @@ fn handle_form_key(form: &mut LoginForm, runtime: &mut Runtime, key: KeyEvent) {
}
fn handle_sso_key(form: &mut LoginForm, runtime: &mut Runtime, key: KeyEvent) {
// Ctrl-shortcuts first: plain letters belong to the paste field.
if key.modifiers.contains(KeyModifiers::CONTROL) {
match key.code {
// Copy the full SSO URL — terminals can't copy a wrapped link
// in one piece, the clipboard can.
KeyCode::Char('l') => {
form.error = None;
match copy_to_clipboard(&form.sso_url) {
Ok(()) => form.error = Some("link copied to clipboard".to_string()),
Err(err) => {
tracing::warn!(%err, "clipboard copy failed");
form.error = Some(format!("copy failed: {err}"));
}
}
}
KeyCode::Char('o') => {
if let Err(err) = open::that_detached(&form.sso_url) {
tracing::warn!(%err, "failed to reopen browser");
form.error = Some("couldn't open a browser".to_string());
}
}
_ => {}
}
return;
}
match key.code {
KeyCode::Esc => {
if let Some(listener) = runtime.sso.take() {
@@ -77,6 +102,10 @@ fn handle_sso_key(form: &mut LoginForm, runtime: &mut Runtime, key: KeyEvent) {
}
}
fn copy_to_clipboard(text: &str) -> Result<(), arboard::Error> {
arboard::Clipboard::new()?.set_text(text.to_string())
}
fn is_typing(key: KeyEvent) -> bool {
key.modifiers
.difference(KeyModifiers::SHIFT)
+15 -16
View File
@@ -66,13 +66,11 @@ fn draw_form(frame: &mut Frame, form: &LoginForm) {
}
fn draw_sso_pending(frame: &mut Frame, form: &LoginForm) {
// The dialog grows with the URL so it is always shown in full and can
// be copied/typed manually.
let width = 78.min(frame.area().width.saturating_sub(2)).max(40);
let inner_width = usize::from(width - 2);
let url_lines = (form.sso_url.len().div_ceil(inner_width.max(1)) as u16).clamp(1, 6);
let height = (13 + url_lines).min(frame.area().height);
let area = centered(frame.area(), width, height);
// The URL stays on ONE line (wrapping breaks copy-paste); the dialog is
// as wide as the terminal allows and ctrl-l copies the full link.
let width = (form.sso_url.len() as u16 + 4)
.clamp(48, frame.area().width.saturating_sub(2).max(40));
let area = centered(frame.area(), width, 14.min(frame.area().height));
let block = Block::bordered()
.title(" Continue with SSO ")
@@ -84,7 +82,7 @@ fn draw_sso_pending(frame: &mut Frame, form: &LoginForm) {
let [steps, url_label, url, paste, message, hint] = Layout::vertical([
Constraint::Length(3),
Constraint::Length(1),
Constraint::Length(url_lines),
Constraint::Length(1),
Constraint::Length(3),
Constraint::Length(2),
Constraint::Length(1),
@@ -111,25 +109,26 @@ fn draw_sso_pending(frame: &mut Frame, form: &LoginForm) {
frame.render_widget(
Paragraph::new(Line::styled(
"If the browser didn't open, visit:",
"If the browser didn't open — ctrl-l copies this link, ctrl-o retries:",
theme::dim(),
)),
url_label,
);
// Unbordered and character-wrapped: the URL is fully visible and can be
// selected in the terminal without picking up border glyphs.
// One line, never wrapped: a wrapped URL copies with a line break and
// stops working. If it doesn't fit, ctrl-l still copies it whole.
frame.render_widget(
Paragraph::new(form.sso_url.clone())
.style(theme::accent())
.wrap(Wrap { trim: false }),
Paragraph::new(Line::styled(form.sso_url.clone(), theme::accent())),
url,
);
draw_field(frame, paste, "Link or code", &form.sso_paste, false, true);
draw_message(frame, message, form);
frame.render_widget(
Paragraph::new(Line::styled("enter submit · esc back · ctrl-c quit", theme::dim()))
.alignment(Alignment::Center),
Paragraph::new(Line::styled(
"enter submit · ctrl-l copy link · esc back · ctrl-c quit",
theme::dim(),
))
.alignment(Alignment::Center),
hint,
);
}