Feat: prelude and new words
Some checks failed
Deploy Website / deploy (push) Failing after 4m48s

This commit is contained in:
2026-02-05 00:58:53 +01:00
parent abafea8ddf
commit de56598fca
21 changed files with 533 additions and 29 deletions

View File

@@ -18,8 +18,8 @@ use crate::page::Page;
use crate::services::{clipboard, dict_nav, euclidean, help_nav, pattern_editor};
use crate::settings::Settings;
use crate::state::{
AudioSettings, CyclicEnum, EditorContext, FlashKind, LiveKeyState, Metrics, Modal,
MuteState, OptionsState, PanelState, PatternField, PatternPropsField, PatternsNav,
AudioSettings, CyclicEnum, EditorContext, EditorTarget, FlashKind, LiveKeyState, Metrics,
Modal, MuteState, OptionsState, PanelState, PatternField, PatternPropsField, PatternsNav,
PlaybackState, ProjectState, StagedChange, StagedPropChange, UiState,
};
@@ -164,8 +164,12 @@ impl App {
self.project_state.mark_all_dirty();
}
pub fn toggle_playing(&mut self) {
pub fn toggle_playing(&mut self, link: &LinkState) {
let was_playing = self.playback.playing;
self.playback.toggle();
if !was_playing && self.playback.playing {
self.evaluate_prelude(link);
}
}
pub fn tempo_up(&self, link: &LinkState) {
@@ -338,6 +342,58 @@ impl App {
self.project_state.mark_dirty(change.bank, change.pattern);
}
pub fn open_prelude_editor(&mut self) {
let prelude = &self.project_state.project.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);
let candidates = model::WORDS
.iter()
.map(|w| cagire_ratatui::CompletionCandidate {
name: w.name.to_string(),
signature: w.stack.to_string(),
description: w.desc.to_string(),
example: w.example.to_string(),
})
.collect();
self.editor_ctx.editor.set_candidates(candidates);
self.editor_ctx
.editor
.set_completion_enabled(self.ui.show_completion);
self.editor_ctx.target = EditorTarget::Prelude;
self.ui.modal = Modal::Editor;
}
pub fn save_prelude(&mut self) {
let text = self.editor_ctx.editor.content();
self.project_state.project.prelude = text;
}
pub fn close_prelude_editor(&mut self) {
self.editor_ctx.target = EditorTarget::Step;
self.load_step_to_editor();
}
pub fn evaluate_prelude(&mut self, link: &LinkState) {
let prelude = &self.project_state.project.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);
}
Err(e) => {
self.ui
.flash(&format!("Prelude error: {e}"), 300, FlashKind::Error);
}
}
}
pub fn execute_script_oneshot(
&self,
script: &str,
@@ -614,6 +670,8 @@ impl App {
self.variables.store(Arc::new(HashMap::new()));
self.dict.lock().clear();
self.evaluate_prelude(link);
for (bank, pattern) in playing {
self.playback.queued_changes.push(StagedChange {
change: PatternChange::Start { bank, pattern },
@@ -862,7 +920,7 @@ impl App {
pub fn dispatch(&mut self, cmd: AppCommand, link: &LinkState, snapshot: &SequencerSnapshot) {
match cmd {
// Playback
AppCommand::TogglePlaying => self.toggle_playing(),
AppCommand::TogglePlaying => self.toggle_playing(link),
AppCommand::TempoUp => self.tempo_up(link),
AppCommand::TempoDown => self.tempo_down(link),
@@ -1278,6 +1336,12 @@ impl App {
FlashKind::Success,
);
}
// Prelude
AppCommand::OpenPreludeEditor => self.open_prelude_editor(),
AppCommand::SavePrelude => self.save_prelude(),
AppCommand::EvaluatePrelude => self.evaluate_prelude(link),
AppCommand::ClosePreludeEditor => self.close_prelude_editor(),
}
}