UI in English
Made-with: Cursor
This commit is contained in:
@@ -12,7 +12,7 @@ use tokio::net::TcpStream as TokioTcpStream;
|
|||||||
/// Prüft die TCP-Verbindung zum Companion (synchron, kurzer Timeout).
|
/// Prüft die TCP-Verbindung zum Companion (synchron, kurzer Timeout).
|
||||||
pub fn check_connection(host: &str, port: u16) -> Result<(), String> {
|
pub fn check_connection(host: &str, port: u16) -> Result<(), String> {
|
||||||
if host.trim().is_empty() {
|
if host.trim().is_empty() {
|
||||||
return Err("Host leer".to_string());
|
return Err("Host empty".to_string());
|
||||||
}
|
}
|
||||||
let addrs: Vec<_> = (host.trim(), port)
|
let addrs: Vec<_> = (host.trim(), port)
|
||||||
.to_socket_addrs()
|
.to_socket_addrs()
|
||||||
@@ -20,7 +20,7 @@ pub fn check_connection(host: &str, port: u16) -> Result<(), String> {
|
|||||||
.collect();
|
.collect();
|
||||||
let addr = addrs
|
let addr = addrs
|
||||||
.first()
|
.first()
|
||||||
.ok_or_else(|| "Host konnte nicht aufgelöst werden".to_string())?;
|
.ok_or_else(|| "Host could not be resolved".to_string())?;
|
||||||
TcpStream::connect_timeout(addr, Duration::from_secs(2)).map_err(|e| e.to_string())?;
|
TcpStream::connect_timeout(addr, Duration::from_secs(2)).map_err(|e| e.to_string())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -40,7 +40,7 @@ pub async fn read_frame(stream: &mut TokioTcpStream) -> std::io::Result<Option<(
|
|||||||
if n < 5 {
|
if n < 5 {
|
||||||
return Err(std::io::Error::new(
|
return Err(std::io::Error::new(
|
||||||
std::io::ErrorKind::UnexpectedEof,
|
std::io::ErrorKind::UnexpectedEof,
|
||||||
"Unvollständiger Companion-Frame-Header",
|
"Incomplete companion frame header",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ pub async fn read_frame(stream: &mut TokioTcpStream) -> std::io::Result<Option<(
|
|||||||
if length > 10 * 1024 * 1024 {
|
if length > 10 * 1024 * 1024 {
|
||||||
return Err(std::io::Error::new(
|
return Err(std::io::Error::new(
|
||||||
std::io::ErrorKind::InvalidData,
|
std::io::ErrorKind::InvalidData,
|
||||||
"Ungültige Companion-Frame-Länge",
|
"Invalid companion frame length",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ pub async fn read_frame(stream: &mut TokioTcpStream) -> std::io::Result<Option<(
|
|||||||
if r == 0 {
|
if r == 0 {
|
||||||
return Err(std::io::Error::new(
|
return Err(std::io::Error::new(
|
||||||
std::io::ErrorKind::UnexpectedEof,
|
std::io::ErrorKind::UnexpectedEof,
|
||||||
"Verbindung während Companion-Frame abgebrochen",
|
"Connection closed during companion frame",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
offset += r;
|
offset += r;
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ fn show_error(msg: &str) {
|
|||||||
.encode_wide()
|
.encode_wide()
|
||||||
.chain(std::iter::once(0))
|
.chain(std::iter::once(0))
|
||||||
.collect();
|
.collect();
|
||||||
let title: Vec<u16> = OsStr::new("HotKeet – Fehler")
|
let title: Vec<u16> = OsStr::new("HotKeet – Error")
|
||||||
.encode_wide()
|
.encode_wide()
|
||||||
.chain(std::iter::once(0))
|
.chain(std::iter::once(0))
|
||||||
.collect();
|
.collect();
|
||||||
@@ -35,7 +35,7 @@ fn show_error(msg: &str) {
|
|||||||
|
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
fn show_error(msg: &str) {
|
fn show_error(msg: &str) {
|
||||||
eprintln!("Fehler: {}", msg);
|
eprintln!("Error: {}", msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
@@ -181,11 +181,11 @@ impl eframe::App for AppState {
|
|||||||
.status
|
.status
|
||||||
.read()
|
.read()
|
||||||
.map(|s| match *s {
|
.map(|s| match *s {
|
||||||
AppStatus::Bereit => "Bereit",
|
AppStatus::Bereit => "Ready",
|
||||||
AppStatus::Aufnahme => "Aufnahme",
|
AppStatus::Aufnahme => "Recording",
|
||||||
AppStatus::Transkribieren => "Transkribieren",
|
AppStatus::Transkribieren => "Transcribing",
|
||||||
AppStatus::Fertig => "Fertig",
|
AppStatus::Fertig => "Done",
|
||||||
AppStatus::Fehler => "Fehler",
|
AppStatus::Fehler => "Error",
|
||||||
})
|
})
|
||||||
.unwrap_or("?");
|
.unwrap_or("?");
|
||||||
let detail = self
|
let detail = self
|
||||||
@@ -198,8 +198,8 @@ impl eframe::App for AppState {
|
|||||||
.read()
|
.read()
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|o| *o)
|
.and_then(|o| *o)
|
||||||
.map(|t| format!("vor {} s", t.elapsed().as_secs()))
|
.map(|t| format!("{} s ago", t.elapsed().as_secs()))
|
||||||
.unwrap_or_else(|| "noch nie empfangen".to_string());
|
.unwrap_or_else(|| "never received".to_string());
|
||||||
|
|
||||||
let companion_status = self
|
let companion_status = self
|
||||||
.companion_status
|
.companion_status
|
||||||
@@ -252,7 +252,7 @@ impl eframe::App for AppState {
|
|||||||
&self.status,
|
&self.status,
|
||||||
&self.status_detail,
|
&self.status_detail,
|
||||||
AppStatus::Fertig,
|
AppStatus::Fertig,
|
||||||
&format!("Eingefügt: {}", preview),
|
&format!("Pasted: {}", preview),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@@ -261,7 +261,7 @@ impl eframe::App for AppState {
|
|||||||
&self.status,
|
&self.status,
|
||||||
&self.status_detail,
|
&self.status_detail,
|
||||||
AppStatus::Fehler,
|
AppStatus::Fehler,
|
||||||
&format!("Einfügen: {}", e),
|
&format!("Paste: {}", e),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -405,12 +405,12 @@ fn main() -> eframe::Result<()> {
|
|||||||
|| err_str.to_lowercase().contains("adapter")
|
|| err_str.to_lowercase().contains("adapter")
|
||||||
{
|
{
|
||||||
"\n\nWindows Server ohne GPU:\n\
|
"\n\nWindows Server ohne GPU:\n\
|
||||||
- opengl32.dll von Mesa3D (fdossena.com/mesa) neben die EXE legen\n\
|
- Place opengl32.dll from Mesa3D (fdossena.com/mesa) next to the EXE\n\
|
||||||
- oder App auf Arbeitsplatz-PC mit GPU ausführen"
|
- Or run app on a workstation PC with GPU"
|
||||||
} else {
|
} else {
|
||||||
""
|
""
|
||||||
};
|
};
|
||||||
show_error(&format!("Start fehlgeschlagen: {}{}", e, hint));
|
show_error(&format!("Start failed: {}{}", e, hint));
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -437,7 +437,7 @@ fn run_recording(
|
|||||||
if config.sound_on_start_end {
|
if config.sound_on_start_end {
|
||||||
sound::play_start(config.debug_logging);
|
sound::play_start(config.debug_logging);
|
||||||
}
|
}
|
||||||
set_status(&status, &status_detail, AppStatus::Aufnahme, "Aufnahme läuft…");
|
set_status(&status, &status_detail, AppStatus::Aufnahme, "Recording…");
|
||||||
|
|
||||||
let use_companion = config.use_companion_microphone && !config.companion_host.is_empty();
|
let use_companion = config.use_companion_microphone && !config.companion_host.is_empty();
|
||||||
if config.debug_logging {
|
if config.debug_logging {
|
||||||
@@ -463,7 +463,7 @@ fn run_recording(
|
|||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("Aufnahme fehlgeschlagen: {}", e);
|
eprintln!("Aufnahme fehlgeschlagen: {}", e);
|
||||||
set_status(&status, &status_detail, AppStatus::Fehler, &format!("Aufnahme: {}", e));
|
set_status(&status, &status_detail, AppStatus::Fehler, &format!("Recording: {}", e));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -485,7 +485,7 @@ fn run_recording(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
set_status(&status, &status_detail, AppStatus::Transkribieren, "Transkribieren…");
|
set_status(&status, &status_detail, AppStatus::Transkribieren, "Transcribing…");
|
||||||
|
|
||||||
if config.sound_on_start_end {
|
if config.sound_on_start_end {
|
||||||
sound::play_end(config.debug_logging);
|
sound::play_end(config.debug_logging);
|
||||||
@@ -508,7 +508,7 @@ fn run_recording(
|
|||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("Transkription: {}", e);
|
eprintln!("Transkription: {}", e);
|
||||||
let _ = std::fs::remove_file(&wav_path);
|
let _ = std::fs::remove_file(&wav_path);
|
||||||
set_status(&status, &status_detail, AppStatus::Fehler, &format!("Transkription: {}", e));
|
set_status(&status, &status_detail, AppStatus::Fehler, &format!("Transcription: {}", e));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -541,6 +541,6 @@ fn run_recording(
|
|||||||
};
|
};
|
||||||
if paste_tx.send(req).is_err() {
|
if paste_tx.send(req).is_err() {
|
||||||
eprintln!("Paste-Kanal geschlossen");
|
eprintln!("Paste-Kanal geschlossen");
|
||||||
set_status(&status, &status_detail, AppStatus::Fehler, "Paste-Kanal fehlgeschlagen");
|
set_status(&status, &status_detail, AppStatus::Fehler, "Paste channel failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ use tokio::net::TcpStream;
|
|||||||
/// Eintrag für die Eingabequellen-Auswahl: (source_index, Anzeigename)
|
/// Eintrag für die Eingabequellen-Auswahl: (source_index, Anzeigename)
|
||||||
/// source_index 0 = Companion-App, 1.. = Mikrofon (input_device_index = source_index - 1)
|
/// source_index 0 = Companion-App, 1.. = Mikrofon (input_device_index = source_index - 1)
|
||||||
pub fn list_input_sources() -> Vec<(usize, String)> {
|
pub fn list_input_sources() -> Vec<(usize, String)> {
|
||||||
let mut sources = vec![(0, "Companion-App".to_string())];
|
let mut sources = vec![(0, "Companion app".to_string())];
|
||||||
|
|
||||||
let devices = match cpal::default_host().input_devices() {
|
let devices = match cpal::default_host().input_devices() {
|
||||||
Ok(d) => d,
|
Ok(d) => d,
|
||||||
@@ -21,8 +21,8 @@ pub fn list_input_sources() -> Vec<(usize, String)> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for (idx, device) in devices.enumerate() {
|
for (idx, device) in devices.enumerate() {
|
||||||
let name = device.name().unwrap_or_else(|_| format!("Mikrofon {}", idx));
|
let name = device.name().unwrap_or_else(|_| format!("Microphone {}", idx));
|
||||||
sources.push((idx + 1, format!("Mikrofon: {}", name)));
|
sources.push((idx + 1, format!("Microphone: {}", name)));
|
||||||
}
|
}
|
||||||
sources
|
sources
|
||||||
}
|
}
|
||||||
@@ -35,8 +35,8 @@ pub fn resolve_input_device_index(config: &DictateConfig) -> usize {
|
|||||||
Err(_) => return config.input_device_index,
|
Err(_) => return config.input_device_index,
|
||||||
};
|
};
|
||||||
for (idx, device) in devices.enumerate() {
|
for (idx, device) in devices.enumerate() {
|
||||||
let name = device.name().unwrap_or_else(|_| format!("Mikrofon {}", idx));
|
let name = device.name().unwrap_or_else(|_| format!("Microphone {}", idx));
|
||||||
let display = format!("Mikrofon: {}", name);
|
let display = format!("Microphone: {}", name);
|
||||||
if display == config.input_device_name {
|
if display == config.input_device_name {
|
||||||
return idx;
|
return idx;
|
||||||
}
|
}
|
||||||
@@ -92,14 +92,14 @@ pub fn record_local(
|
|||||||
let device_index = resolve_input_device_index(config);
|
let device_index = resolve_input_device_index(config);
|
||||||
let device = host
|
let device = host
|
||||||
.input_devices()
|
.input_devices()
|
||||||
.map_err(|e| format!("Kein Audiogerät: {}", e))?
|
.map_err(|e| format!("No audio device: {}", e))?
|
||||||
.nth(device_index)
|
.nth(device_index)
|
||||||
.or_else(|| host.default_input_device())
|
.or_else(|| host.default_input_device())
|
||||||
.ok_or("Kein Eingabegerät gefunden")?;
|
.ok_or("No input device found")?;
|
||||||
|
|
||||||
let supported = device
|
let supported = device
|
||||||
.default_input_config()
|
.default_input_config()
|
||||||
.map_err(|e| format!("Kein Input-Config: {}", e))?;
|
.map_err(|e| format!("No input config: {}", e))?;
|
||||||
|
|
||||||
let sample_rate = supported.sample_rate();
|
let sample_rate = supported.sample_rate();
|
||||||
let collector = Arc::new(SampleCollector::new());
|
let collector = Arc::new(SampleCollector::new());
|
||||||
@@ -143,7 +143,7 @@ pub fn record_local(
|
|||||||
)
|
)
|
||||||
.map_err(|e| format!("Stream build: {}", e))?
|
.map_err(|e| format!("Stream build: {}", e))?
|
||||||
}
|
}
|
||||||
_ => return Err("Nur I16/F32 unterstützt".to_string()),
|
_ => return Err("Only I16/F32 supported".to_string()),
|
||||||
};
|
};
|
||||||
|
|
||||||
stream.play().map_err(|e| format!("Stream play: {}", e))?;
|
stream.play().map_err(|e| format!("Stream play: {}", e))?;
|
||||||
@@ -195,7 +195,7 @@ pub async fn record_companion(
|
|||||||
let addr = format!("{}:{}", host, port);
|
let addr = format!("{}:{}", host, port);
|
||||||
let mut stream = TcpStream::connect(&addr)
|
let mut stream = TcpStream::connect(&addr)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| format!("Companion verbinden: {}", e))?;
|
.map_err(|e| format!("Companion connect: {}", e))?;
|
||||||
|
|
||||||
let mut all_audio: Vec<i16> = Vec::new();
|
let mut all_audio: Vec<i16> = Vec::new();
|
||||||
let start = std::time::Instant::now();
|
let start = std::time::Instant::now();
|
||||||
@@ -233,10 +233,10 @@ pub fn write_wav(path: &std::path::Path, samples: &[i16]) -> Result<(), String>
|
|||||||
sample_format: hound::SampleFormat::Int,
|
sample_format: hound::SampleFormat::Int,
|
||||||
};
|
};
|
||||||
let mut writer = hound::WavWriter::create(path, spec)
|
let mut writer = hound::WavWriter::create(path, spec)
|
||||||
.map_err(|e| format!("WAV erstellen: {}", e))?;
|
.map_err(|e| format!("Create WAV: {}", e))?;
|
||||||
for &s in samples {
|
for &s in samples {
|
||||||
writer.write_sample(s).map_err(|e| format!("WAV schreiben: {}", e))?;
|
writer.write_sample(s).map_err(|e| format!("Write WAV: {}", e))?;
|
||||||
}
|
}
|
||||||
writer.finalize().map_err(|e| format!("WAV finalisieren: {}", e))?;
|
writer.finalize().map_err(|e| format!("Finalize WAV: {}", e))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,15 +43,15 @@ pub fn transcribe(
|
|||||||
|
|
||||||
let output = cmd
|
let output = cmd
|
||||||
.output()
|
.output()
|
||||||
.map_err(|e| format!("parakeet-cli starten: {}", e))?;
|
.map_err(|e| format!("Start parakeet-cli: {}", e))?;
|
||||||
|
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||||
return Err(format!("parakeet-cli Fehler: {}", stderr));
|
return Err(format!("parakeet-cli error: {}", stderr));
|
||||||
}
|
}
|
||||||
|
|
||||||
let stdout = String::from_utf8(output.stdout)
|
let stdout = String::from_utf8(output.stdout)
|
||||||
.map_err(|_| "parakeet-cli Ausgabe ist kein UTF-8")?;
|
.map_err(|_| "parakeet-cli output is not UTF-8")?;
|
||||||
|
|
||||||
let json: serde_json::Value = serde_json::from_str(stdout.trim())
|
let json: serde_json::Value = serde_json::from_str(stdout.trim())
|
||||||
.map_err(|e| format!("parakeet-cli JSON: {}", e))?;
|
.map_err(|e| format!("parakeet-cli JSON: {}", e))?;
|
||||||
|
|||||||
@@ -32,12 +32,12 @@ pub fn create_tray(tx: Sender<TrayMessage>) -> Option<tray_item::TrayItem> {
|
|||||||
.ok()?;
|
.ok()?;
|
||||||
|
|
||||||
let tx_show = tx.clone();
|
let tx_show = tx.clone();
|
||||||
tray.add_menu_item("Einstellungen", move || {
|
tray.add_menu_item("Settings", move || {
|
||||||
let _ = tx_show.send(TrayMessage::ShowSettings);
|
let _ = tx_show.send(TrayMessage::ShowSettings);
|
||||||
})
|
})
|
||||||
.ok()?;
|
.ok()?;
|
||||||
|
|
||||||
tray.add_menu_item("Beenden", move || {
|
tray.add_menu_item("Quit", move || {
|
||||||
let _ = tx.send(TrayMessage::Quit);
|
let _ = tx.send(TrayMessage::Quit);
|
||||||
})
|
})
|
||||||
.ok()?;
|
.ok()?;
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ impl SettingsApp {
|
|||||||
companion_status: &Option<Result<(), String>>,
|
companion_status: &Option<Result<(), String>>,
|
||||||
) {
|
) {
|
||||||
egui::CentralPanel::default().show(ctx, |ui| {
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
ui.heading("HotKeet – Einstellungen");
|
ui.heading("HotKeet – Settings");
|
||||||
|
|
||||||
ui.add_space(8.0);
|
ui.add_space(8.0);
|
||||||
|
|
||||||
@@ -84,10 +84,10 @@ impl SettingsApp {
|
|||||||
ui.label(status_detail);
|
ui.label(status_detail);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ui.label(format!("Letzter Hotkey: {}", hotkey_info));
|
ui.label(format!("Last hotkey: {}", hotkey_info));
|
||||||
|
|
||||||
if let Some(ref tx) = self.test_tx {
|
if let Some(ref tx) = self.test_tx {
|
||||||
if ui.button("Test: Aufnahme starten (3 s)").clicked() {
|
if ui.button("Test: Start recording (3 s)").clicked() {
|
||||||
let _ = tx.send(());
|
let _ = tx.send(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -97,7 +97,7 @@ impl SettingsApp {
|
|||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.label("Hotkey:");
|
ui.label("Hotkey:");
|
||||||
let label = if self.hotkey_capturing {
|
let label = if self.hotkey_capturing {
|
||||||
"Taste drücken… (Esc = Abbrechen)"
|
"Press key… (Esc = Cancel)"
|
||||||
} else {
|
} else {
|
||||||
&self.config.global_hotkey
|
&self.config.global_hotkey
|
||||||
};
|
};
|
||||||
@@ -119,7 +119,7 @@ impl SettingsApp {
|
|||||||
hotkey::format_hotkey(mods.ctrl, mods.shift, mods.alt, key.name())
|
hotkey::format_hotkey(mods.ctrl, mods.shift, mods.alt, key.name())
|
||||||
{
|
{
|
||||||
self.config.global_hotkey = s;
|
self.config.global_hotkey = s;
|
||||||
self.status = "Änderungen speichern nicht vergessen.".to_string();
|
self.status = "Remember to save changes.".to_string();
|
||||||
self.hotkey_capturing = false;
|
self.hotkey_capturing = false;
|
||||||
}
|
}
|
||||||
// Nur bei gültiger Haupttaste abbrechen, sonst weiter (Modifier überspringen)
|
// Nur bei gültiger Haupttaste abbrechen, sonst weiter (Modifier überspringen)
|
||||||
@@ -134,7 +134,7 @@ impl SettingsApp {
|
|||||||
|
|
||||||
ui.add_space(4.0);
|
ui.add_space(4.0);
|
||||||
|
|
||||||
ui.label("Eingabequelle:");
|
ui.label("Input source:");
|
||||||
let sources = list_input_sources();
|
let sources = list_input_sources();
|
||||||
let mut selected = self.selected_source_index(&sources);
|
let mut selected = self.selected_source_index(&sources);
|
||||||
let selected = &mut selected;
|
let selected = &mut selected;
|
||||||
@@ -164,11 +164,11 @@ impl SettingsApp {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
match companion_status {
|
match companion_status {
|
||||||
None => ui.label("Prüfe Verbindung…"),
|
None => ui.label("Checking connection…"),
|
||||||
Some(Ok(())) => ui.label("Verbunden"),
|
Some(Ok(())) => ui.label("Connected"),
|
||||||
Some(Err(e)) => ui.colored_label(
|
Some(Err(e)) => ui.colored_label(
|
||||||
egui::Color32::RED,
|
egui::Color32::RED,
|
||||||
format!("Nicht verbunden: {}", e),
|
format!("Not connected: {}", e),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -176,87 +176,87 @@ impl SettingsApp {
|
|||||||
ui.add_space(4.0);
|
ui.add_space(4.0);
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.label("parakeet-cli Pfad:");
|
ui.label("parakeet-cli path:");
|
||||||
let display = if self.config.parakeet_cli_path.is_empty() {
|
let display = if self.config.parakeet_cli_path.is_empty() {
|
||||||
"(leer = im PATH)".to_string()
|
"(empty = in PATH)".to_string()
|
||||||
} else {
|
} else {
|
||||||
self.config.parakeet_cli_path.clone()
|
self.config.parakeet_cli_path.clone()
|
||||||
};
|
};
|
||||||
ui.label(egui::RichText::new(&display).color(egui::Color32::GRAY));
|
ui.label(egui::RichText::new(&display).color(egui::Color32::GRAY));
|
||||||
if ui.button("Durchsuchen…").clicked() {
|
if ui.button("Browse…").clicked() {
|
||||||
let mut dialog = rfd::FileDialog::new().set_title("parakeet-cli auswählen");
|
let mut dialog = rfd::FileDialog::new().set_title("Select parakeet-cli");
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
{
|
{
|
||||||
dialog = dialog.add_filter("Executable", &["exe"]);
|
dialog = dialog.add_filter("Executable", &["exe"]);
|
||||||
}
|
}
|
||||||
dialog = dialog.add_filter("Alle Dateien", &["*"]);
|
dialog = dialog.add_filter("All files", &["*"]);
|
||||||
if let Some(p) = dialog.pick_file() {
|
if let Some(p) = dialog.pick_file() {
|
||||||
self.config.parakeet_cli_path = p.display().to_string();
|
self.config.parakeet_cli_path = p.display().to_string();
|
||||||
self.status = "Änderungen speichern nicht vergessen.".to_string();
|
self.status = "Remember to save changes.".to_string();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !self.config.parakeet_cli_path.is_empty() && ui.small_button("✕").clicked() {
|
if !self.config.parakeet_cli_path.is_empty() && ui.small_button("✕").clicked() {
|
||||||
self.config.parakeet_cli_path.clear();
|
self.config.parakeet_cli_path.clear();
|
||||||
self.status = "Änderungen speichern nicht vergessen.".to_string();
|
self.status = "Remember to save changes.".to_string();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.label("Modellpfad:");
|
ui.label("Model path:");
|
||||||
let display = if self.config.model_path.is_empty() {
|
let display = if self.config.model_path.is_empty() {
|
||||||
"(leer = Standardpfad)".to_string()
|
"(empty = default path)".to_string()
|
||||||
} else {
|
} else {
|
||||||
self.config.model_path.clone()
|
self.config.model_path.clone()
|
||||||
};
|
};
|
||||||
ui.label(egui::RichText::new(&display).color(egui::Color32::GRAY));
|
ui.label(egui::RichText::new(&display).color(egui::Color32::GRAY));
|
||||||
if ui.button("Durchsuchen…").clicked() {
|
if ui.button("Browse…").clicked() {
|
||||||
if let Some(p) = rfd::FileDialog::new()
|
if let Some(p) = rfd::FileDialog::new()
|
||||||
.set_title("Modellordner auswählen")
|
.set_title("Select model folder")
|
||||||
.pick_folder()
|
.pick_folder()
|
||||||
{
|
{
|
||||||
self.config.model_path = p.display().to_string();
|
self.config.model_path = p.display().to_string();
|
||||||
self.status = "Änderungen speichern nicht vergessen.".to_string();
|
self.status = "Remember to save changes.".to_string();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !self.config.model_path.is_empty() && ui.small_button("✕").clicked() {
|
if !self.config.model_path.is_empty() && ui.small_button("✕").clicked() {
|
||||||
self.config.model_path.clear();
|
self.config.model_path.clear();
|
||||||
self.status = "Änderungen speichern nicht vergessen.".to_string();
|
self.status = "Remember to save changes.".to_string();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ui.add_space(4.0);
|
ui.add_space(4.0);
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.label("Einfügemethode:");
|
ui.label("Paste method:");
|
||||||
let mut method = self.config.paste_method.clone();
|
let mut method = self.config.paste_method.clone();
|
||||||
egui::ComboBox::from_id_salt("paste_method")
|
egui::ComboBox::from_id_salt("paste_method")
|
||||||
.selected_text(&method)
|
.selected_text(&method)
|
||||||
.show_ui(ui, |ui| {
|
.show_ui(ui, |ui| {
|
||||||
ui.selectable_value(&mut method, "Auto".to_string(), "Auto (Tastaturpuffer, Fallback Clipboard)");
|
ui.selectable_value(&mut method, "Auto".to_string(), "Auto (keyboard buffer, fallback clipboard)");
|
||||||
ui.selectable_value(&mut method, "Keyboard".to_string(), "Nur Tastaturpuffer");
|
ui.selectable_value(&mut method, "Keyboard".to_string(), "Keyboard only");
|
||||||
ui.selectable_value(&mut method, "Clipboard".to_string(), "Nur Zwischenablage");
|
ui.selectable_value(&mut method, "Clipboard".to_string(), "Clipboard only");
|
||||||
});
|
});
|
||||||
self.config.paste_method = method;
|
self.config.paste_method = method;
|
||||||
});
|
});
|
||||||
|
|
||||||
ui.add_space(4.0);
|
ui.add_space(4.0);
|
||||||
|
|
||||||
ui.checkbox(&mut self.config.start_minimized, "Beim Start minimieren");
|
ui.checkbox(&mut self.config.start_minimized, "Minimize on start");
|
||||||
ui.checkbox(&mut self.config.minimize_to_tray, "In Tray minimieren");
|
ui.checkbox(&mut self.config.minimize_to_tray, "Minimize to tray");
|
||||||
ui.checkbox(&mut self.config.sound_on_start_end, "Signaltöne bei Start und Ende des Diktats");
|
ui.checkbox(&mut self.config.sound_on_start_end, "Audio feedback on record start/end");
|
||||||
ui.checkbox(&mut self.config.debug_logging, "Debug-Logging (paste-debug.log, Konsole)");
|
ui.checkbox(&mut self.config.debug_logging, "Debug logging (paste-debug.log, console)");
|
||||||
|
|
||||||
ui.add_space(16.0);
|
ui.add_space(16.0);
|
||||||
|
|
||||||
if ui.button("Speichern").clicked() {
|
if ui.button("Save").clicked() {
|
||||||
match self.config.save() {
|
match self.config.save() {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
if let Some(ref arc) = self.config_arc {
|
if let Some(ref arc) = self.config_arc {
|
||||||
let _ = arc.write().map(|mut w| *w = self.config.clone());
|
let _ = arc.write().map(|mut w| *w = self.config.clone());
|
||||||
}
|
}
|
||||||
self.status = "Gespeichert.".to_string();
|
self.status = "Saved.".to_string();
|
||||||
}
|
}
|
||||||
Err(e) => self.status = format!("Fehler: {}", e),
|
Err(e) => self.status = format!("Error: {}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user