formalize native dialogs into prompt_text

Signed-off-by: FTCHD <144691102+FTCHD@users.noreply.github.com>
This commit is contained in:
FTCHD
2025-09-18 13:43:32 +02:00
parent d856234daa
commit 33dc6276a9
2 changed files with 127 additions and 67 deletions
+2 -2
View File
@@ -427,10 +427,10 @@ export async function provisionRclone() {
*/
async function promptForConfigPassword(configLabel: string): Promise<string | null> {
try {
const result = (await invoke('prompt_password', {
const result = await invoke<string | null>('prompt_password', {
title: 'Rclone UI',
message: `Please enter the password for the encrypted configuration "${configLabel}".`,
})) as string | null
})
if (typeof result === 'string') {
return result.trim()
+125 -65
View File
@@ -182,144 +182,204 @@ fn get_uid() -> String {
#[tauri::command]
async fn prompt_password(title: String, message: String) -> Result<Option<String>, String> {
prompt_text(title, message, None, Some(true)).await
}
#[tauri::command]
async fn prompt_text(
title: String,
message: String,
default: Option<String>,
sensitive: Option<bool>,
) -> Result<Option<String>, String> {
#[cfg(target_os = "macos")]
{
use std::process::Command;
let script = format!(
r#"display dialog "{}" with title "{}" default answer "" with hidden answer"#,
message.replace("\"", "\\\""),
title.replace("\"", "\\\"")
);
let default_value = default.unwrap_or_default();
let is_sensitive = sensitive.unwrap_or(false);
let script = if is_sensitive {
format!(
r#"display dialog "{}" with title "{}" default answer "{}" with hidden answer"#,
message.replace("\"", "\\\""),
title.replace("\"", "\\\""),
default_value.replace("\"", "\\\""),
)
} else {
format!(
r#"display dialog "{}" with title "{}" default answer "{}""#,
message.replace("\"", "\\\""),
title.replace("\"", "\\\""),
default_value.replace("\"", "\\\""),
)
};
let output = Command::new("osascript")
.arg("-e")
.arg(&script)
.output()
.map_err(|e| e.to_string())?;
if output.status.success() {
let result = String::from_utf8_lossy(&output.stdout);
// Parse AppleScript result: "text returned:password, button returned:OK"
if let Some(password_part) = result.split("text returned:").nth(1) {
if let Some(password) = password_part.split(", button returned:").next() {
return Ok(Some(password.to_string()));
// Parse AppleScript result: "text returned:VALUE, button returned:OK"
if let Some(text_part) = result.split("text returned:").nth(1) {
if let Some(value) = text_part.split(", button returned:").next() {
return Ok(Some(value.to_string()));
}
}
}
Ok(None)
return Ok(None);
}
#[cfg(target_os = "windows")]
{
use std::process::Command;
// Use PowerShell to create a credential dialog on Windows
let default_value = default.unwrap_or_default();
let is_sensitive = sensitive.unwrap_or(false);
// Use PowerShell to create a simple text input dialog
let ps_default = default_value.replace('\'', "''");
let ps_title = title.replace('\'', "''");
let ps_message = message.replace('\'', "''");
let ps_password_flag = if is_sensitive { "$true" } else { "$false" };
let powershell_script = format!(
r#"
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
$form = New-Object System.Windows.Forms.Form
$form.Text = '{}'
$form.Size = New-Object System.Drawing.Size(350, 200)
$form.Text = '{title}'
$form.Size = New-Object System.Drawing.Size(350, 180)
$form.StartPosition = 'CenterScreen'
$form.FormBorderStyle = 'FixedDialog'
$form.MaximizeBox = $false
$form.MinimizeBox = $false
$form.TopMost = $true
$label = New-Object System.Windows.Forms.Label
$label.Location = New-Object System.Drawing.Point(10, 20)
$label.Location = New-Object System.Drawing.Point(10, 15)
$label.Size = New-Object System.Drawing.Size(320, 40)
$label.Text = '{}'
$label.Text = '{message}'
$form.Controls.Add($label)
$textBox = New-Object System.Windows.Forms.TextBox
$textBox.Location = New-Object System.Drawing.Point(10, 70)
$textBox.Location = New-Object System.Drawing.Point(10, 60)
$textBox.Size = New-Object System.Drawing.Size(320, 20)
$textBox.UseSystemPasswordChar = $true
$textBox.Text = '{default}'
$textBox.UseSystemPasswordChar = {password}
$form.Controls.Add($textBox)
$okButton = New-Object System.Windows.Forms.Button
$okButton.Location = New-Object System.Drawing.Point(175, 110)
$okButton.Location = New-Object System.Drawing.Point(175, 100)
$okButton.Size = New-Object System.Drawing.Size(75, 23)
$okButton.Text = 'OK'
$okButton.DialogResult = [System.Windows.Forms.DialogResult]::OK
$form.AcceptButton = $okButton
$form.Controls.Add($okButton)
$cancelButton = New-Object System.Windows.Forms.Button
$cancelButton.Location = New-Object System.Drawing.Point(255, 110)
$cancelButton.Location = New-Object System.Drawing.Point(255, 100)
$cancelButton.Size = New-Object System.Drawing.Size(75, 23)
$cancelButton.Text = 'Cancel'
$cancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
$form.CancelButton = $cancelButton
$form.Controls.Add($cancelButton)
$form.Add_Shown({{$textBox.Select()}})
$result = $form.ShowDialog()
if ($result -eq [System.Windows.Forms.DialogResult]::OK) {{
$textBox.Text
}}
"#,
title.replace("'", "''"),
message.replace("'", "''")
title = ps_title,
message = ps_message,
default = ps_default,
password = ps_password_flag
);
let output = Command::new("powershell")
.args(&["-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", &powershell_script])
.output()
.map_err(|e| e.to_string())?;
if output.status.success() {
let result = String::from_utf8_lossy(&output.stdout).trim().to_string();
if !result.is_empty() {
if !result.is_empty() || !default_value.is_empty() {
return Ok(Some(result));
}
}
Ok(None)
return Ok(None);
}
#[cfg(target_os = "linux")]
{
// Try different methods for Linux
// First try zenity (most common)
if let Ok(output) = std::process::Command::new("zenity")
.args(&["--password", "--title", &title, "--text", &message])
.output()
{
if output.status.success() {
let result = String::from_utf8_lossy(&output.stdout).trim().to_string();
if !result.is_empty() {
let default_value = default.unwrap_or_default();
let is_sensitive = sensitive.unwrap_or(false);
if is_sensitive {
// Try zenity password dialog first
if let Ok(output) = std::process::Command::new("zenity")
.args(&["--password", "--title", &title, "--text", &message])
.output()
{
if output.status.success() {
let result = String::from_utf8_lossy(&output.stdout).trim().to_string();
if !result.is_empty() {
return Ok(Some(result));
}
}
}
// Fallback to kdialog password
if let Ok(output) = std::process::Command::new("kdialog")
.args(&["--password", &message, "--title", &title])
.output()
{
if output.status.success() {
let result = String::from_utf8_lossy(&output.stdout).trim().to_string();
if !result.is_empty() {
return Ok(Some(result));
}
}
}
return Err("No suitable password dialog found. Please install zenity or kdialog.".to_string());
} else {
// Try zenity text entry first
if let Ok(output) = std::process::Command::new("zenity")
.args(&["--entry", "--title", &title, "--text", &message, "--entry-text", &default_value])
.output()
{
if output.status.success() {
let result = String::from_utf8_lossy(&output.stdout).trim().to_string();
return Ok(Some(result));
}
}
}
// Fallback to kdialog (KDE)
if let Ok(output) = std::process::Command::new("kdialog")
.args(&["--password", &message, "--title", &title])
.output()
{
if output.status.success() {
let result = String::from_utf8_lossy(&output.stdout).trim().to_string();
if !result.is_empty() {
// Fallback to kdialog input box
if let Ok(output) = std::process::Command::new("kdialog")
.args(&["--inputbox", &message, &default_value, "--title", &title])
.output()
{
if output.status.success() {
let result = String::from_utf8_lossy(&output.stdout).trim().to_string();
return Ok(Some(result));
}
}
return Err("No suitable input dialog found. Please install zenity or kdialog.".to_string());
}
Err("No suitable password dialog found. Please install zenity or kdialog.".to_string())
}
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "linux")))]
{
Err("Password input not supported on this platform".to_string())
Err("Text input not supported on this platform".to_string())
}
}
@@ -521,7 +581,7 @@ pub fn run() {
.plugin(tauri_plugin_log::Builder::new().build())
.plugin(tauri_plugin_shell::init())
.plugin(tauri_plugin_opener::init())
.invoke_handler(tauri::generate_handler![unzip_file, get_arch, get_uid, prompt_password, stop_pid, update_system_rclone, test_proxy_connection])
.invoke_handler(tauri::generate_handler![unzip_file, get_arch, get_uid, prompt_password, prompt_text, stop_pid, update_system_rclone, test_proxy_connection])
.setup(|_app| Ok(()))
// .setup(|app| {
// if cfg!(debug_assertions) {