From f3af161969ab6ca1bd2166dad24009a2fb7eedee Mon Sep 17 00:00:00 2001 From: Elray02 Date: Wed, 8 Apr 2026 18:39:38 +0200 Subject: [PATCH 1/4] feat/manageLogs/ add pagacakge for manage better application logs --- Cargo.lock | 34 ++++++++++++++++++++++++++++++++++ Cargo.toml | 2 ++ 2 files changed, 36 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 754e1a1..aee041d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1496,6 +1496,17 @@ dependencies = [ "libc", ] +[[package]] +name = "simplelog" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16257adbfaef1ee58b1363bdc0664c9b8e1e30aed86049635fb5f147d065a9c0" +dependencies = [ + "log", + "termcolor", + "time", +] + [[package]] name = "siphasher" version = "1.0.2" @@ -1569,6 +1580,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + [[package]] name = "terminfo" version = "0.9.0" @@ -1639,9 +1659,11 @@ dependencies = [ "cpal", "crossbeam-channel", "crossterm", + "log", "ratatui", "serde", "serde_json", + "simplelog", ] [[package]] @@ -1691,12 +1713,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" dependencies = [ "deranged", + "itoa", "libc", "num-conv", "num_threads", "powerfmt", "serde_core", "time-core", + "time-macros", ] [[package]] @@ -1705,6 +1729,16 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" +[[package]] +name = "time-macros" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +dependencies = [ + "num-conv", + "time-core", +] + [[package]] name = "toml_datetime" version = "1.0.0+spec-1.1.0" diff --git a/Cargo.toml b/Cargo.toml index 493316e..a6604bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,3 +10,5 @@ cpal = "0.17" crossbeam-channel = "0.5" serde = { version = "1", features = ["derive"] } serde_json = "1" +log = "0.4" +simplelog = "0.12" From 0b920c267a3a602dadb3e3377e07beb56d840ab9 Mon Sep 17 00:00:00 2001 From: Elray02 Date: Wed, 8 Apr 2026 18:40:29 +0200 Subject: [PATCH 2/4] feat/manageLogs/write logs inside a dedicated file --- src/main.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index ad489c5..32a4e74 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,6 +21,18 @@ use crossterm::terminal::{self, EnterAlternateScreen, LeaveAlternateScreen}; use ratatui::backend::CrosstermBackend; use ratatui::Terminal; +/// Redirect all log output to `~/.local/share/textstep/textstep.log` so that +/// runtime diagnostics never corrupt the TUI display. Errors are silently +/// swallowed if the file cannot be created (e.g. read-only FS). +fn init_logger() { + use simplelog::{Config, LevelFilter, WriteLogger}; + let log_dir = sequencer::project::data_dir(); + let _ = std::fs::create_dir_all(&log_dir); + if let Ok(file) = std::fs::File::create(log_dir.join("textstep.log")) { + let _ = WriteLogger::init(LevelFilter::Warn, Config::default(), file); + } +} + /// Sets up cross-thread channels, starts the audio stream, initializes the /// terminal, and enters the main UI loop. The audio stream is kept alive /// until this function returns. @@ -32,6 +44,8 @@ fn main() -> io::Result<()> { return Ok(()); } + init_logger(); + // Create channels for UI <-> Audio communication let (tx_to_audio, rx_from_ui) = crossbeam_channel::bounded(64); let (tx_to_ui, rx_from_audio) = crossbeam_channel::bounded(16); @@ -43,7 +57,7 @@ fn main() -> io::Result<()> { let _stream = match audio::start_audio_stream(rx_from_ui, tx_to_ui, Arc::clone(&display_buf)) { Ok(s) => s, Err(e) => { - eprintln!("Audio error: {e}"); + log::error!("Audio error: {e}"); return Ok(()); } }; From edab5d3fd5578d25991a45fe2acc263cea06e12c Mon Sep 17 00:00:00 2001 From: Elray02 Date: Wed, 8 Apr 2026 18:41:32 +0200 Subject: [PATCH 3/4] feat/manageLogs/use new metho for manage logs --- src/audio/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/audio/mod.rs b/src/audio/mod.rs index 944a60a..646cbe1 100644 --- a/src/audio/mod.rs +++ b/src/audio/mod.rs @@ -65,7 +65,7 @@ pub fn start_audio_stream( } }, |err| { - eprintln!("audio stream error: {}", err); + log::error!("audio stream error: {}", err); }, None, ) From 9ad85ca26a988c20d2b8eb0c23fc9e54e3f9a3ca Mon Sep 17 00:00:00 2001 From: Elray02 Date: Wed, 8 Apr 2026 18:45:24 +0200 Subject: [PATCH 4/4] feat/updateKnowbsValue/ remap shortCut for update value of the knobs --- src/keys.rs | 85 +++++++++++++++++++----------------------- src/ui/help_overlay.rs | 6 +-- 2 files changed, 41 insertions(+), 50 deletions(-) diff --git a/src/keys.rs b/src/keys.rs index 366aa30..417aea3 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -818,51 +818,38 @@ fn handle_knobs(app: &mut App, key: KeyEvent) { let has_alt = key.modifiers.contains(KeyModifiers::ALT); match key.code { + KeyCode::Left if has_shift => { + // Shift+Left: change track up + app.ui.drum_ctrl_track = if t == 0 { NUM_DRUM_TRACKS - 1 } else { t - 1 }; + app.ui.drum_cursor_track = app.ui.drum_ctrl_track; + } + KeyCode::Right if has_shift => { + // Shift+Right: change track down + app.ui.drum_ctrl_track = (t + 1) % NUM_DRUM_TRACKS; + app.ui.drum_cursor_track = app.ui.drum_ctrl_track; + } KeyCode::Left => { app.ui.drum_ctrl_field = app.ui.drum_ctrl_field.prev_knob(); } KeyCode::Right => { app.ui.drum_ctrl_field = app.ui.drum_ctrl_field.next_knob(); } - KeyCode::Up if has_shift || has_alt => { + KeyCode::Up => { + // Up always increases the selected knob value adjust_drum_field(app, t, PARAM_INCREMENT); if has_alt { let _ = app.tx_to_audio.send(UiToAudio::TriggerDrum(TRACK_IDS[t])); app.ui.flash_track(t); } } - KeyCode::Down if has_shift || has_alt => { + KeyCode::Down => { + // Down always decreases the selected knob value adjust_drum_field(app, t, -PARAM_INCREMENT); if has_alt { let _ = app.tx_to_audio.send(UiToAudio::TriggerDrum(TRACK_IDS[t])); app.ui.flash_track(t); } } - KeyCode::Up => { - // Move between row 1 (0-4) and row 2 (5-9), or change track at edge - if let Some(idx) = app.ui.drum_ctrl_field.knob_index() { - if idx >= 5 { - // Row 2 -> Row 1 (same column) - app.ui.drum_ctrl_field = crate::app::KNOB_FIELDS[idx - 5]; - } else { - // Row 1 -> change track up - app.ui.drum_ctrl_track = if t == 0 { NUM_DRUM_TRACKS - 1 } else { t - 1 }; - app.ui.drum_cursor_track = app.ui.drum_ctrl_track; - } - } - } - KeyCode::Down => { - if let Some(idx) = app.ui.drum_ctrl_field.knob_index() { - if idx < 5 { - // Row 1 -> Row 2 (same column) - app.ui.drum_ctrl_field = crate::app::KNOB_FIELDS[idx + 5]; - } else { - // Row 2 -> change track down - app.ui.drum_ctrl_track = (t + 1) % NUM_DRUM_TRACKS; - app.ui.drum_cursor_track = app.ui.drum_ctrl_track; - } - } - } _ => {} } } @@ -1051,25 +1038,8 @@ fn handle_synth_controls(app: &mut App, key: KeyEvent, synth_id: SynthId) { ui.ctrl_field = SYNTH_CTRL_ROWS[r][c + 1]; } } - KeyCode::Up if has_shift || has_alt => { - adjust_synth_field(app, synth_id, PARAM_INCREMENT); - if has_alt { - let ui = synth_ui_mut(app, synth_id); - let note = ui.octave * 12 + 12; - ui.flash = 6; - let _ = app.tx_to_audio.send(UiToAudio::TriggerSynth(synth_id, note)); - } - } - KeyCode::Down if has_shift || has_alt => { - adjust_synth_field(app, synth_id, -PARAM_INCREMENT); - if has_alt { - let ui = synth_ui_mut(app, synth_id); - let note = ui.octave * 12 + 12; - ui.flash = 6; - let _ = app.tx_to_audio.send(UiToAudio::TriggerSynth(synth_id, note)); - } - } - KeyCode::Up => { + KeyCode::Up if has_shift => { + // Shift+Up: navigate to row above let ui = synth_ui_mut(app, synth_id); let (r, c) = find_synth_field_pos(ui.ctrl_field); if r > 0 { @@ -1078,7 +1048,8 @@ fn handle_synth_controls(app: &mut App, key: KeyEvent, synth_id: SynthId) { ui.ctrl_field = new_row[new_c]; } } - KeyCode::Down => { + KeyCode::Down if has_shift => { + // Shift+Down: navigate to row below let ui = synth_ui_mut(app, synth_id); let (r, c) = find_synth_field_pos(ui.ctrl_field); if r + 1 < SYNTH_CTRL_ROWS.len() { @@ -1087,6 +1058,26 @@ fn handle_synth_controls(app: &mut App, key: KeyEvent, synth_id: SynthId) { ui.ctrl_field = new_row[new_c]; } } + KeyCode::Up => { + // Up always increases the selected control's value + adjust_synth_field(app, synth_id, PARAM_INCREMENT); + if has_alt { + let ui = synth_ui_mut(app, synth_id); + let note = ui.octave * 12 + 12; + ui.flash = 6; + let _ = app.tx_to_audio.send(UiToAudio::TriggerSynth(synth_id, note)); + } + } + KeyCode::Down => { + // Down always decreases the selected control's value + adjust_synth_field(app, synth_id, -PARAM_INCREMENT); + if has_alt { + let ui = synth_ui_mut(app, synth_id); + let note = ui.octave * 12 + 12; + ui.flash = 6; + let _ = app.tx_to_audio.send(UiToAudio::TriggerSynth(synth_id, note)); + } + } _ => {} } } diff --git a/src/ui/help_overlay.rs b/src/ui/help_overlay.rs index 219ed36..a23dcf7 100644 --- a/src/ui/help_overlay.rs +++ b/src/ui/help_overlay.rs @@ -72,19 +72,19 @@ pub fn render_help(f: &mut Frame, area: Rect) { "Shift+Tab", "Prev section", "Shift+S", "Solo track"), row3("- / =", "BPM -1 / +1", - "Arrows", "Move cursor", + "Arrows", "Move/adjust", "Shift+V", "Master volume"), row3("Shift+- / +","BPM -10 / +10", "Enter", "Toggle step", "Shift+C", "Compressor"), row3("` (backtick)","Record on/off", - "Shift+Up/Dn","Adjust value", + "Up/Down", "Adjust value", "Shift+T", "Tube saturator"), row3("l", "Loop on/off", "Alt+Up/Dn", "Adjust+audition", "Alt+R", "Randomize"), row3("Shift+L", "Loop length", - ";", "Page SYN/AMP/FX", + "Shift+Up/Dn","Row nav (synth)", "F2", "Toggle synths"), row3("< / >", "Swing ±5%", "~", "Spectrum/VU",