Feat: mixed bag
Some checks failed
Deploy Website / deploy (push) Failing after 2m9s

This commit is contained in:
2026-02-25 20:31:36 +01:00
parent 6b94d6403a
commit 0f0f13f2b8
25 changed files with 246 additions and 10 deletions

View File

@@ -55,13 +55,17 @@ pub fn bindings_for(page: Page, plugin_mode: bool) -> Vec<(&'static str, &'stati
}
Page::Patterns => {
bindings.push(("←→↑↓", "Navigate", "Move between banks/patterns"));
bindings.push(("Shift+↑↓", "Select", "Extend selection"));
bindings.push(("Alt+↑↓", "Shift", "Move patterns up/down"));
bindings.push(("Enter", "Select", "Select pattern for editing"));
if !plugin_mode {
bindings.push(("Space", "Play", "Toggle pattern playback"));
}
bindings.push(("Esc", "Back", "Clear staged or go back"));
bindings.push(("c", "Commit", "Commit staged changes"));
bindings.push(("p", "Stage play", "Stage pattern play toggle"));
bindings.push(("r", "Rename", "Rename bank/pattern"));
bindings.push(("d", "Describe", "Add description to pattern"));
bindings.push(("e", "Properties", "Edit pattern properties"));
bindings.push(("m", "Mute", "Stage mute for pattern"));
bindings.push(("x", "Solo", "Stage solo for pattern"));
@@ -70,6 +74,8 @@ pub fn bindings_for(page: Page, plugin_mode: bool) -> Vec<(&'static str, &'stati
bindings.push(("Ctrl+C", "Copy", "Copy bank/pattern"));
bindings.push(("Ctrl+V", "Paste", "Paste bank/pattern"));
bindings.push(("Del", "Reset", "Reset bank/pattern"));
bindings.push(("Ctrl+Z", "Undo", "Undo last action"));
bindings.push(("Ctrl+Shift+Z", "Redo", "Redo last action"));
}
Page::Engine => {
bindings.push(("Tab", "Section", "Next section"));

View File

@@ -496,13 +496,22 @@ fn render_patterns(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, a
height: 1,
};
let label = format!(
let right_label = format!(
"{} · {}",
pattern.quantization.label(),
pattern.sync_mode.label()
);
let w = detail_area.width as usize;
let padded_label = format!("{label:>w$}");
let label = if let Some(desc) = &pattern.description {
let right_len = right_label.chars().count();
let max_desc = w.saturating_sub(right_len + 1);
let truncated: String = desc.chars().take(max_desc).collect();
let pad = w.saturating_sub(truncated.chars().count() + right_len);
format!("{truncated}{}{right_label}", " ".repeat(pad))
} else {
format!("{right_label:>w$}")
};
let padded_label = label;
let filled_width = if is_playing {
let ratio = snapshot.get_smooth_progress(bank, idx, length, speed.multiplier()).unwrap_or(0.0);
@@ -725,6 +734,7 @@ fn render_properties(
let pattern = &app.project_state.project.banks[bank].patterns[pattern_idx];
let name = pattern.name.as_deref().unwrap_or("-");
let desc = pattern.description.as_deref().unwrap_or("-");
let content_count = pattern.content_step_count();
let steps_label = format!("{}/{}", content_count, pattern.length);
let speed_label = pattern.speed.label();
@@ -739,6 +749,10 @@ fn render_properties(
Span::styled(" Name ", label_style),
Span::styled(name, value_style),
]),
Line::from(vec![
Span::styled(" Desc ", label_style),
Span::styled(desc, value_style),
]),
Line::from(vec![
Span::styled(" Steps ", label_style),
Span::styled(steps_label, value_style),

View File

@@ -650,6 +650,7 @@ fn render_modal(
pattern,
field,
name,
description,
length,
speed,
quantization,
@@ -660,7 +661,7 @@ fn render_modal(
use crate::state::PatternPropsField;
let is_chain = matches!(follow_up, FollowUp::Chain { .. });
let modal_height = if is_chain { 16 } else { 14 };
let modal_height = if is_chain { 18 } else { 16 };
let inner = ModalFrame::new(&format!(" Pattern B{:02}:P{:02} ", bank + 1, pattern + 1))
.width(50)
@@ -678,6 +679,7 @@ fn render_modal(
};
let mut fields: Vec<(&str, String, bool)> = vec![
("Name", name.clone(), *field == PatternPropsField::Name),
("Desc", description.clone(), *field == PatternPropsField::Description),
("Length", length.clone(), *field == PatternPropsField::Length),
("Speed", speed_label, *field == PatternPropsField::Speed),
("Quantization", quantization.label().to_string(), *field == PatternPropsField::Quantization),