Wip
This commit is contained in:
@@ -469,6 +469,13 @@ impl App {
|
||||
AppCommand::SavePrelude => self.save_prelude(),
|
||||
AppCommand::EvaluatePrelude => self.evaluate_prelude(link),
|
||||
AppCommand::ClosePreludeEditor => self.close_prelude_editor(),
|
||||
AppCommand::OpenBankPreludeEditor => self.open_bank_prelude_editor(),
|
||||
AppCommand::SaveBankPrelude => self.save_bank_prelude(),
|
||||
AppCommand::EvaluateBankPrelude => {
|
||||
let bank = self.editor_ctx.bank;
|
||||
self.evaluate_bank_prelude(bank, link);
|
||||
}
|
||||
AppCommand::CloseBankPreludeEditor => self.close_bank_prelude_editor(),
|
||||
|
||||
// Periodic script
|
||||
AppCommand::OpenScriptModal(field) => self.open_script_modal(field),
|
||||
|
||||
@@ -109,24 +109,101 @@ impl App {
|
||||
self.load_step_to_editor();
|
||||
}
|
||||
|
||||
/// Evaluate the project prelude to seed variables and definitions.
|
||||
pub fn evaluate_prelude(&mut self, link: &LinkState) {
|
||||
let prelude = &self.project_state.project.prelude;
|
||||
/// Switch the editor to the current bank's prelude script.
|
||||
pub fn open_bank_prelude_editor(&mut self) {
|
||||
let bank = self.editor_ctx.bank;
|
||||
let prelude = &self.project_state.project.banks[bank].prelude;
|
||||
let lines: Vec<String> = if prelude.is_empty() {
|
||||
vec![String::new()]
|
||||
} else {
|
||||
prelude.lines().map(String::from).collect()
|
||||
};
|
||||
self.editor_ctx.editor.set_content(lines);
|
||||
self.editor_ctx.editor.set_candidates(Arc::clone(&COMPLETION_CANDIDATES));
|
||||
self.editor_ctx
|
||||
.editor
|
||||
.set_completion_enabled(self.ui.show_completion);
|
||||
let tree = SampleTree::from_paths(&self.audio.config.sample_paths);
|
||||
self.editor_ctx.editor.set_sample_folders(tree.all_folder_names());
|
||||
self.editor_ctx.target = EditorTarget::BankPrelude;
|
||||
self.ui.modal = Modal::Editor;
|
||||
}
|
||||
|
||||
pub fn save_bank_prelude(&mut self) {
|
||||
let bank = self.editor_ctx.bank;
|
||||
let text = self.editor_ctx.editor.content();
|
||||
self.project_state.project.banks[bank].prelude = text;
|
||||
}
|
||||
|
||||
pub fn close_bank_prelude_editor(&mut self) {
|
||||
self.editor_ctx.target = EditorTarget::Step;
|
||||
self.load_step_to_editor();
|
||||
}
|
||||
|
||||
/// Evaluate a single bank's prelude.
|
||||
pub fn evaluate_bank_prelude(&mut self, bank: usize, link: &LinkState) {
|
||||
let prelude = &self.project_state.project.banks[bank].prelude;
|
||||
if prelude.trim().is_empty() {
|
||||
return;
|
||||
}
|
||||
let ctx = self.create_step_context(0, link);
|
||||
match self.script_engine.evaluate(prelude, &ctx) {
|
||||
Ok(_) => {
|
||||
self.ui.flash("Prelude evaluated", 150, FlashKind::Info);
|
||||
}
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
self.ui
|
||||
.flash(&format!("Prelude error: {e}"), 300, FlashKind::Error);
|
||||
let fallback = format!("Bank {}", bank + 1);
|
||||
let bank_name = self.project_state.project.banks[bank]
|
||||
.name
|
||||
.as_deref()
|
||||
.unwrap_or(&fallback);
|
||||
self.ui.flash(
|
||||
&format!("{bank_name} prelude error: {e}"),
|
||||
300,
|
||||
FlashKind::Error,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluate the project prelude and all bank preludes.
|
||||
pub fn evaluate_prelude(&mut self, link: &LinkState) {
|
||||
let project_prelude = &self.project_state.project.prelude;
|
||||
if !project_prelude.trim().is_empty() {
|
||||
let ctx = self.create_step_context(0, link);
|
||||
match self.script_engine.evaluate(project_prelude, &ctx) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
self.ui
|
||||
.flash(&format!("Project prelude error: {e}"), 300, FlashKind::Error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
for bank_idx in 0..self.project_state.project.banks.len() {
|
||||
let prelude = &self.project_state.project.banks[bank_idx].prelude;
|
||||
if prelude.trim().is_empty() {
|
||||
continue;
|
||||
}
|
||||
let ctx = self.create_step_context(0, link);
|
||||
match self.script_engine.evaluate(prelude, &ctx) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
let bank_name = self.project_state.project.banks[bank_idx]
|
||||
.name
|
||||
.as_deref()
|
||||
.map(String::from)
|
||||
.unwrap_or_else(|| format!("Bank {}", bank_idx + 1));
|
||||
self.ui.flash(
|
||||
&format!("{bank_name} prelude error: {e}"),
|
||||
300,
|
||||
FlashKind::Error,
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.ui.flash("Preludes evaluated", 150, FlashKind::Info);
|
||||
}
|
||||
|
||||
/// Evaluate a script and immediately send its audio commands.
|
||||
/// Returns collected `print` output, if any.
|
||||
pub fn execute_script_oneshot(
|
||||
|
||||
@@ -42,6 +42,11 @@ impl App {
|
||||
let data = self.project_state.project.pattern_at(*bank, *pattern).clone();
|
||||
Some(UndoScope::Pattern { bank: *bank, pattern: *pattern, data })
|
||||
}
|
||||
AppCommand::SaveBankPrelude => {
|
||||
let bank = self.editor_ctx.bank;
|
||||
let data = self.project_state.project.banks[bank].clone();
|
||||
Some(UndoScope::Bank { bank, data })
|
||||
}
|
||||
AppCommand::ResetBank { bank }
|
||||
| AppCommand::PasteBank { bank }
|
||||
| AppCommand::ImportBank { bank } => {
|
||||
|
||||
@@ -301,6 +301,10 @@ pub enum AppCommand {
|
||||
SavePrelude,
|
||||
EvaluatePrelude,
|
||||
ClosePreludeEditor,
|
||||
OpenBankPreludeEditor,
|
||||
SaveBankPrelude,
|
||||
EvaluateBankPrelude,
|
||||
CloseBankPreludeEditor,
|
||||
|
||||
// Onboarding
|
||||
DismissOnboarding,
|
||||
|
||||
@@ -82,7 +82,8 @@ pub(super) fn handle_main_page(ctx: &mut InputContext, key: KeyEvent, ctrl: bool
|
||||
KeyCode::Char(']') => ctx.dispatch(AppCommand::SpeedIncrease),
|
||||
KeyCode::Char('L') => ctx.dispatch(AppCommand::OpenPatternModal(PatternField::Length)),
|
||||
KeyCode::Char('S') => ctx.dispatch(AppCommand::OpenPatternModal(PatternField::Speed)),
|
||||
KeyCode::Char('p') => ctx.dispatch(AppCommand::OpenPreludeEditor),
|
||||
KeyCode::Char('p') => ctx.dispatch(AppCommand::OpenBankPreludeEditor),
|
||||
KeyCode::Char('P') => ctx.dispatch(AppCommand::OpenPreludeEditor),
|
||||
KeyCode::Delete | KeyCode::Backspace => {
|
||||
let (bank, pattern) = (ctx.app.editor_ctx.bank, ctx.app.editor_ctx.pattern);
|
||||
if let Some(range) = ctx.app.editor_ctx.selection_range() {
|
||||
|
||||
@@ -351,6 +351,11 @@ pub(super) fn handle_modal_input(ctx: &mut InputContext, key: KeyEvent) -> Input
|
||||
ctx.dispatch(AppCommand::EvaluatePrelude);
|
||||
ctx.dispatch(AppCommand::ClosePreludeEditor);
|
||||
}
|
||||
EditorTarget::BankPrelude => {
|
||||
ctx.dispatch(AppCommand::SaveBankPrelude);
|
||||
ctx.dispatch(AppCommand::EvaluateBankPrelude);
|
||||
ctx.dispatch(AppCommand::CloseBankPreludeEditor);
|
||||
}
|
||||
}
|
||||
ctx.dispatch(AppCommand::CloseModal);
|
||||
}
|
||||
@@ -364,6 +369,10 @@ pub(super) fn handle_modal_input(ctx: &mut InputContext, key: KeyEvent) -> Input
|
||||
ctx.dispatch(AppCommand::SavePrelude);
|
||||
ctx.dispatch(AppCommand::EvaluatePrelude);
|
||||
}
|
||||
EditorTarget::BankPrelude => {
|
||||
ctx.dispatch(AppCommand::SaveBankPrelude);
|
||||
ctx.dispatch(AppCommand::EvaluateBankPrelude);
|
||||
}
|
||||
},
|
||||
KeyCode::Char('b') if ctrl => {
|
||||
editor.activate_sample_finder();
|
||||
|
||||
@@ -941,6 +941,11 @@ fn handle_modal_click(ctx: &mut InputContext, col: u16, row: u16, term: Rect) {
|
||||
ctx.dispatch(AppCommand::EvaluatePrelude);
|
||||
ctx.dispatch(AppCommand::ClosePreludeEditor);
|
||||
}
|
||||
EditorTarget::BankPrelude => {
|
||||
ctx.dispatch(AppCommand::SaveBankPrelude);
|
||||
ctx.dispatch(AppCommand::EvaluateBankPrelude);
|
||||
ctx.dispatch(AppCommand::CloseBankPreludeEditor);
|
||||
}
|
||||
}
|
||||
ctx.dispatch(AppCommand::CloseModal);
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ pub const DOCS: &[DocEntry] = &[
|
||||
),
|
||||
Topic("Brackets", include_str!("../../docs/forth/brackets.md")),
|
||||
Topic("Cycling", include_str!("../../docs/forth/cycling.md")),
|
||||
Topic("The Prelude", include_str!("../../docs/forth/prelude.md")),
|
||||
Topic("Preludes", include_str!("../../docs/forth/prelude.md")),
|
||||
Topic(
|
||||
"Cagire vs Classic",
|
||||
include_str!("../../docs/forth/oddities.md"),
|
||||
|
||||
@@ -766,17 +766,26 @@ pub static COMMANDS: LazyLock<Vec<CommandEntry>> = LazyLock::new(|| {
|
||||
|
||||
// === Prelude ===
|
||||
CommandEntry {
|
||||
name: "Edit Prelude",
|
||||
description: "Open prelude editor",
|
||||
name: "Edit Bank Prelude",
|
||||
description: "Open current bank's prelude editor",
|
||||
category: "Prelude",
|
||||
keybinding: "p",
|
||||
pages: &[Main],
|
||||
normal_mode: false,
|
||||
action: Some(PaletteAction::Resolve(|_| Some(AppCommand::OpenBankPreludeEditor))),
|
||||
},
|
||||
CommandEntry {
|
||||
name: "Edit Project Prelude",
|
||||
description: "Open project-wide prelude editor",
|
||||
category: "Prelude",
|
||||
keybinding: "P",
|
||||
pages: &[Main],
|
||||
normal_mode: false,
|
||||
action: Some(PaletteAction::Resolve(|_| Some(AppCommand::OpenPreludeEditor))),
|
||||
},
|
||||
CommandEntry {
|
||||
name: "Evaluate Prelude",
|
||||
description: "Re-evaluate prelude script",
|
||||
name: "Evaluate All Preludes",
|
||||
description: "Re-evaluate project and all bank preludes",
|
||||
category: "Prelude",
|
||||
keybinding: "d",
|
||||
pages: &[Main],
|
||||
|
||||
@@ -8,6 +8,7 @@ pub enum EditorTarget {
|
||||
#[default]
|
||||
Step,
|
||||
Prelude,
|
||||
BankPrelude,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
|
||||
@@ -76,7 +76,11 @@ fn render_top_layout(
|
||||
}
|
||||
if has_preview {
|
||||
let user_words: HashSet<String> = app.dict.lock().keys().cloned().collect();
|
||||
let has_prelude = !app.project_state.project.prelude.trim().is_empty();
|
||||
let has_prelude = !app.project_state.project.prelude.trim().is_empty()
|
||||
|| !app.project_state.project.banks[app.editor_ctx.bank]
|
||||
.prelude
|
||||
.trim()
|
||||
.is_empty();
|
||||
if has_prelude {
|
||||
let [script_area, prelude_area] =
|
||||
Layout::horizontal([Constraint::Fill(1), Constraint::Fill(1)]).areas(areas[idx]);
|
||||
@@ -195,7 +199,11 @@ fn render_viz_area(
|
||||
VizPanel::Lissajous => render_lissajous(frame, app, *panel_area),
|
||||
VizPanel::Preview => {
|
||||
let user_words = user_words_once.as_ref().expect("user_words initialized");
|
||||
let has_prelude = !app.project_state.project.prelude.trim().is_empty();
|
||||
let has_prelude = !app.project_state.project.prelude.trim().is_empty()
|
||||
|| !app.project_state.project.banks[app.editor_ctx.bank]
|
||||
.prelude
|
||||
.trim()
|
||||
.is_empty();
|
||||
if has_prelude {
|
||||
let [script_area, prelude_area] = if is_vertical_layout {
|
||||
Layout::vertical([Constraint::Fill(1), Constraint::Fill(1)])
|
||||
@@ -655,11 +663,20 @@ pub(crate) fn render_prelude_preview(
|
||||
area: Rect,
|
||||
) {
|
||||
let theme = theme::get();
|
||||
let prelude = &app.project_state.project.prelude;
|
||||
let bank_prelude = &app.project_state.project.banks[app.editor_ctx.bank].prelude;
|
||||
let (prelude, title) = if !bank_prelude.trim().is_empty() {
|
||||
let bank_name = app.project_state.project.banks[app.editor_ctx.bank]
|
||||
.name
|
||||
.as_deref()
|
||||
.unwrap_or("Bank");
|
||||
(bank_prelude.as_str(), format!(" {bank_name} Prelude "))
|
||||
} else {
|
||||
(app.project_state.project.prelude.as_str(), " Prelude ".to_string())
|
||||
};
|
||||
|
||||
let block = Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.title(" Prelude ")
|
||||
.title(title)
|
||||
.border_style(Style::new().fg(theme.ui.border));
|
||||
let inner = block.inner(area);
|
||||
frame.render_widget(block, area);
|
||||
|
||||
@@ -911,7 +911,13 @@ fn render_modal_editor(
|
||||
};
|
||||
|
||||
let title = match app.editor_ctx.target {
|
||||
EditorTarget::Prelude => "Prelude".to_string(),
|
||||
EditorTarget::Prelude => "Project Prelude".to_string(),
|
||||
EditorTarget::BankPrelude => {
|
||||
let bank = &app.project_state.project.banks[app.editor_ctx.bank];
|
||||
let fallback = format!("Bank {}", app.editor_ctx.bank + 1);
|
||||
let bank_name = bank.name.as_deref().unwrap_or(&fallback);
|
||||
format!("{bank_name} Prelude")
|
||||
}
|
||||
EditorTarget::Step => {
|
||||
let step_num = app.editor_ctx.step + 1;
|
||||
let step = app.current_edit_pattern().step(app.editor_ctx.step);
|
||||
|
||||
@@ -114,7 +114,11 @@ fn render_sidebar(frame: &mut Frame, app: &App, area: Rect) {
|
||||
if app.audio.config.show_lissajous {
|
||||
constraints.push(Constraint::Fill(1));
|
||||
}
|
||||
let has_prelude = !app.project_state.project.prelude.trim().is_empty();
|
||||
let has_prelude = !app.project_state.project.prelude.trim().is_empty()
|
||||
|| !app.project_state.project.banks[app.editor_ctx.bank]
|
||||
.prelude
|
||||
.trim()
|
||||
.is_empty();
|
||||
if has_prelude {
|
||||
constraints.push(Constraint::Fill(1));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user