Clean plugins

This commit is contained in:
2026-02-21 01:27:32 +01:00
parent e9bca2548c
commit 77d5235d92
12 changed files with 284 additions and 167 deletions

View File

@@ -17,7 +17,9 @@ pub fn bindings_for(page: Page, plugin_mode: bool) -> Vec<(&'static str, &'stati
// Page-specific bindings
match page {
Page::Main => {
bindings.push(("Space", "Play/Stop", "Toggle playback"));
if !plugin_mode {
bindings.push(("Space", "Play/Stop", "Toggle playback"));
}
bindings.push(("←→↑↓", "Navigate", "Move cursor between steps"));
bindings.push(("Shift+←→↑↓", "Select", "Extend selection"));
bindings.push(("Esc", "Clear", "Clear selection"));
@@ -33,8 +35,10 @@ pub fn bindings_for(page: Page, plugin_mode: bool) -> Vec<(&'static str, &'stati
bindings.push(("Del", "Delete", "Delete step(s)"));
bindings.push(("< >", "Length", "Decrease/increase pattern length"));
bindings.push(("[ ]", "Speed", "Decrease/increase pattern speed"));
bindings.push(("+ -", "Tempo", "Increase/decrease tempo"));
bindings.push(("T", "Set tempo", "Open tempo input"));
if !plugin_mode {
bindings.push(("+ -", "Tempo", "Increase/decrease tempo"));
bindings.push(("T", "Set tempo", "Open tempo input"));
}
bindings.push(("L", "Set length", "Open length input"));
bindings.push(("S", "Set speed", "Open speed input"));
bindings.push(("f", "Fill", "Toggle fill mode (hold)"));
@@ -52,7 +56,9 @@ pub fn bindings_for(page: Page, plugin_mode: bool) -> Vec<(&'static str, &'stati
Page::Patterns => {
bindings.push(("←→↑↓", "Navigate", "Move between banks/patterns"));
bindings.push(("Enter", "Select", "Select pattern for editing"));
bindings.push(("Space", "Play", "Toggle pattern playback"));
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(("r", "Rename", "Rename bank/pattern"));
@@ -72,20 +78,26 @@ pub fn bindings_for(page: Page, plugin_mode: bool) -> Vec<(&'static str, &'stati
bindings.push(("↑↓", "Navigate", "Navigate list items"));
bindings.push(("PgUp/Dn", "Page", "Page through device list"));
bindings.push(("Enter", "Select", "Select device"));
bindings.push(("R", "Restart", "Restart audio engine"));
if !plugin_mode {
bindings.push(("R", "Restart", "Restart audio engine"));
}
bindings.push(("A", "Add path", "Add sample path"));
bindings.push(("D", "Refresh/Del", "Refresh devices or delete path"));
bindings.push(("h", "Hush", "Stop all sounds gracefully"));
bindings.push(("p", "Panic", "Stop all sounds immediately"));
bindings.push(("r", "Reset", "Reset peak voice counter"));
bindings.push(("t", "Test", "Play test tone"));
if !plugin_mode {
bindings.push(("t", "Test", "Play test tone"));
}
}
Page::Options => {
bindings.push(("Tab", "Next", "Move to next option"));
bindings.push(("Shift+Tab", "Previous", "Move to previous option"));
bindings.push(("↑↓", "Navigate", "Navigate options"));
bindings.push(("←→", "Toggle", "Toggle or adjust option"));
bindings.push(("Space", "Play/Stop", "Toggle playback"));
if !plugin_mode {
bindings.push(("Space", "Play/Stop", "Toggle playback"));
}
}
Page::Help => {
bindings.push(("↑↓ j/k", "Scroll", "Scroll content"));

View File

@@ -32,84 +32,6 @@ pub fn render(frame: &mut Frame, app: &App, link: &LinkState, area: Rect) {
let focus = app.options.focus;
let content_width = padded.width as usize;
let enabled = link.is_enabled();
let peers = link.peers();
let (status_text, status_color) = if !enabled {
("DISABLED", theme.link_status.disabled)
} else if peers > 0 {
("CONNECTED", theme.link_status.connected)
} else {
("LISTENING", theme.link_status.listening)
};
let peer_text = if enabled && peers > 0 {
if peers == 1 {
" · 1 peer".to_string()
} else {
format!(" · {peers} peers")
}
} else {
String::new()
};
let link_header = Line::from(vec![
Span::styled(
"ABLETON LINK",
Style::new().fg(theme.ui.header).add_modifier(Modifier::BOLD),
),
Span::raw(" "),
Span::styled(
status_text,
Style::new().fg(status_color).add_modifier(Modifier::BOLD),
),
Span::styled(peer_text, Style::new().fg(theme.ui.text_muted)),
]);
let quantum_str = format!("{:.0}", link.quantum());
let tempo_str = format!("{:.1} BPM", link.tempo());
let beat_str = format!("{:.2}", link.beat());
let phase_str = format!("{:.2}", link.phase());
let tempo_style = Style::new().fg(theme.values.tempo).add_modifier(Modifier::BOLD);
let value_style = Style::new().fg(theme.values.value);
let midi_outputs = midi::list_midi_outputs();
let midi_inputs = midi::list_midi_inputs();
let midi_out_display = |slot: usize| -> String {
if let Some(idx) = app.midi.selected_outputs[slot] {
midi_outputs
.get(idx)
.map(|d| d.name.clone())
.unwrap_or_else(|| "(disconnected)".to_string())
} else if midi_outputs.is_empty() {
"(none found)".to_string()
} else {
"(not connected)".to_string()
}
};
let midi_in_display = |slot: usize| -> String {
if let Some(idx) = app.midi.selected_inputs[slot] {
midi_inputs
.get(idx)
.map(|d| d.name.clone())
.unwrap_or_else(|| "(disconnected)".to_string())
} else if midi_inputs.is_empty() {
"(none found)".to_string()
} else {
"(not connected)".to_string()
}
};
let midi_out_0 = midi_out_display(0);
let midi_out_1 = midi_out_display(1);
let midi_out_2 = midi_out_display(2);
let midi_out_3 = midi_out_display(3);
let midi_in_0 = midi_in_display(0);
let midi_in_1 = midi_in_display(1);
let midi_in_2 = midi_in_display(2);
let midi_in_3 = midi_in_display(3);
let onboarding_str = format!("{}/6 dismissed", app.ui.onboarding_dismissed.len());
let hue_str = format!("{}°", app.ui.hue_rotation as i32);
@@ -169,9 +91,9 @@ pub fn render(frame: &mut Frame, app: &App, link: &LinkState, area: Rect) {
&theme,
),
];
let zoom_str = format!("{:.0}%", app.ui.zoom_factor * 100.0);
let window_str = format!("{}x{}", app.ui.window_width, app.ui.window_height);
if app.plugin_mode {
let zoom_str = format!("{:.0}%", app.ui.zoom_factor * 100.0);
let window_str = format!("{}x{}", app.ui.window_width, app.ui.window_height);
lines.push(render_option_line(
"Font",
&app.ui.font,
@@ -191,48 +113,127 @@ pub fn render(frame: &mut Frame, app: &App, link: &LinkState, area: Rect) {
&theme,
));
}
if !app.plugin_mode {
let enabled = link.is_enabled();
let peers = link.peers();
let (status_text, status_color) = if !enabled {
("DISABLED", theme.link_status.disabled)
} else if peers > 0 {
("CONNECTED", theme.link_status.connected)
} else {
("LISTENING", theme.link_status.listening)
};
let peer_text = if enabled && peers > 0 {
if peers == 1 {
" · 1 peer".to_string()
} else {
format!(" · {peers} peers")
}
} else {
String::new()
};
let link_header = Line::from(vec![
Span::styled(
"ABLETON LINK",
Style::new().fg(theme.ui.header).add_modifier(Modifier::BOLD),
),
Span::raw(" "),
Span::styled(
status_text,
Style::new().fg(status_color).add_modifier(Modifier::BOLD),
),
Span::styled(peer_text, Style::new().fg(theme.ui.text_muted)),
]);
let quantum_str = format!("{:.0}", link.quantum());
let tempo_str = format!("{:.1} BPM", link.tempo());
let beat_str = format!("{:.2}", link.beat());
let phase_str = format!("{:.2}", link.phase());
let tempo_style = Style::new().fg(theme.values.tempo).add_modifier(Modifier::BOLD);
let value_style = Style::new().fg(theme.values.value);
lines.push(Line::from(""));
lines.extend([
link_header,
render_divider(content_width, &theme),
render_option_line(
"Enabled",
if link.is_enabled() { "On" } else { "Off" },
focus == OptionsFocus::LinkEnabled,
&theme,
),
render_option_line(
"Start/Stop sync",
if link.is_start_stop_sync_enabled() {
"On"
} else {
"Off"
},
focus == OptionsFocus::StartStopSync,
&theme,
),
render_option_line("Quantum", &quantum_str, focus == OptionsFocus::Quantum, &theme),
Line::from(""),
render_section_header("SESSION", &theme),
render_divider(content_width, &theme),
render_readonly_line("Tempo", &tempo_str, tempo_style, &theme),
render_readonly_line("Beat", &beat_str, value_style, &theme),
render_readonly_line("Phase", &phase_str, value_style, &theme),
]);
let midi_outputs = midi::list_midi_outputs();
let midi_inputs = midi::list_midi_inputs();
let midi_out_display = |slot: usize| -> String {
if let Some(idx) = app.midi.selected_outputs[slot] {
midi_outputs
.get(idx)
.map(|d| d.name.clone())
.unwrap_or_else(|| "(disconnected)".to_string())
} else if midi_outputs.is_empty() {
"(none found)".to_string()
} else {
"(not connected)".to_string()
}
};
let midi_in_display = |slot: usize| -> String {
if let Some(idx) = app.midi.selected_inputs[slot] {
midi_inputs
.get(idx)
.map(|d| d.name.clone())
.unwrap_or_else(|| "(disconnected)".to_string())
} else if midi_inputs.is_empty() {
"(none found)".to_string()
} else {
"(not connected)".to_string()
}
};
let midi_out_0 = midi_out_display(0);
let midi_out_1 = midi_out_display(1);
let midi_out_2 = midi_out_display(2);
let midi_out_3 = midi_out_display(3);
let midi_in_0 = midi_in_display(0);
let midi_in_1 = midi_in_display(1);
let midi_in_2 = midi_in_display(2);
let midi_in_3 = midi_in_display(3);
lines.push(Line::from(""));
lines.extend([
render_section_header("MIDI OUTPUTS", &theme),
render_divider(content_width, &theme),
render_option_line("Output 0", &midi_out_0, focus == OptionsFocus::MidiOutput0, &theme),
render_option_line("Output 1", &midi_out_1, focus == OptionsFocus::MidiOutput1, &theme),
render_option_line("Output 2", &midi_out_2, focus == OptionsFocus::MidiOutput2, &theme),
render_option_line("Output 3", &midi_out_3, focus == OptionsFocus::MidiOutput3, &theme),
Line::from(""),
render_section_header("MIDI INPUTS", &theme),
render_divider(content_width, &theme),
render_option_line("Input 0", &midi_in_0, focus == OptionsFocus::MidiInput0, &theme),
render_option_line("Input 1", &midi_in_1, focus == OptionsFocus::MidiInput1, &theme),
render_option_line("Input 2", &midi_in_2, focus == OptionsFocus::MidiInput2, &theme),
render_option_line("Input 3", &midi_in_3, focus == OptionsFocus::MidiInput3, &theme),
]);
}
lines.push(Line::from(""));
lines.extend([
link_header,
render_divider(content_width, &theme),
render_option_line(
"Enabled",
if link.is_enabled() { "On" } else { "Off" },
focus == OptionsFocus::LinkEnabled,
&theme,
),
render_option_line(
"Start/Stop sync",
if link.is_start_stop_sync_enabled() {
"On"
} else {
"Off"
},
focus == OptionsFocus::StartStopSync,
&theme,
),
render_option_line("Quantum", &quantum_str, focus == OptionsFocus::Quantum, &theme),
Line::from(""),
render_section_header("SESSION", &theme),
render_divider(content_width, &theme),
render_readonly_line("Tempo", &tempo_str, tempo_style, &theme),
render_readonly_line("Beat", &beat_str, value_style, &theme),
render_readonly_line("Phase", &phase_str, value_style, &theme),
Line::from(""),
render_section_header("MIDI OUTPUTS", &theme),
render_divider(content_width, &theme),
render_option_line("Output 0", &midi_out_0, focus == OptionsFocus::MidiOutput0, &theme),
render_option_line("Output 1", &midi_out_1, focus == OptionsFocus::MidiOutput1, &theme),
render_option_line("Output 2", &midi_out_2, focus == OptionsFocus::MidiOutput2, &theme),
render_option_line("Output 3", &midi_out_3, focus == OptionsFocus::MidiOutput3, &theme),
Line::from(""),
render_section_header("MIDI INPUTS", &theme),
render_divider(content_width, &theme),
render_option_line("Input 0", &midi_in_0, focus == OptionsFocus::MidiInput0, &theme),
render_option_line("Input 1", &midi_in_1, focus == OptionsFocus::MidiInput1, &theme),
render_option_line("Input 2", &midi_in_2, focus == OptionsFocus::MidiInput2, &theme),
render_option_line("Input 3", &midi_in_3, focus == OptionsFocus::MidiInput3, &theme),
Line::from(""),
render_section_header("ONBOARDING", &theme),
render_divider(content_width, &theme),
render_option_line(
@@ -290,7 +291,7 @@ fn render_divider(width: usize, theme: &theme::ThemeColors) -> Line<'static> {
))
}
fn render_option_line<'a>(label: &'a str, value: &'a str, focused: bool, theme: &theme::ThemeColors) -> Line<'a> {
fn render_option_line(label: &str, value: &str, focused: bool, theme: &theme::ThemeColors) -> Line<'static> {
let highlight = Style::new().fg(theme.hint.key).add_modifier(Modifier::BOLD);
let normal = Style::new().fg(theme.ui.text_primary);
let label_style = Style::new().fg(theme.ui.text_muted);
@@ -309,19 +310,19 @@ fn render_option_line<'a>(label: &'a str, value: &'a str, focused: bool, theme:
let padded_label = format!("{label:<label_width$}");
Line::from(vec![
Span::styled(prefix, prefix_style),
Span::styled(prefix.to_string(), prefix_style),
Span::styled(padded_label, label_style),
Span::styled(value_display, value_style),
])
}
fn render_readonly_line<'a>(label: &'a str, value: &'a str, value_style: Style, theme: &theme::ThemeColors) -> Line<'a> {
fn render_readonly_line(label: &str, value: &str, value_style: Style, theme: &theme::ThemeColors) -> Line<'static> {
let label_style = Style::new().fg(theme.ui.text_muted);
let label_width = 20;
let padded_label = format!("{label:<label_width$}");
Line::from(vec![
Span::raw(" "),
Span::styled(" ".to_string(), Style::new()),
Span::styled(padded_label, label_style),
Span::styled(format!(" {value}"), value_style),
])