fix: project reload on rebuild and syntax check now gives toast
This commit is contained in:
@@ -46,6 +46,7 @@ Invariants and behaviours in the allium spec should be covered by unit tests of
|
||||
- there are no "pre-existing" problems - you own every problem, you fix every problem
|
||||
- don't leave unused code in the codebase, remove it instead
|
||||
- after implementing / changing things, run the build and run tests to verify all works
|
||||
- run `cargo test --workspace` with permission for its loopback test servers on the first attempt; AI mock-server and preview-server tests bind localhost and otherwise fail under sandboxing, causing a pointless rerun
|
||||
- do not reference external JavaScript or CSS on CDNs, always bring it into the project
|
||||
- do not embedd CSS/JavaScript into HTML, always reference .css and .js files in the project assets
|
||||
- always make sure you follow proper i18n best practices. no untranslated string constants.
|
||||
|
||||
@@ -9,7 +9,7 @@ The project is under active development. Core blogging workflows are broadly ava
|
||||
- Native Iced desktop workspace with localized menus, tabs, sidebars, dialogs, tasks, and embedded Wry previews.
|
||||
- Post and translation authoring with draft/published lifecycle, metadata, tags, categories, links, media, and batch gallery-image import.
|
||||
- Media import, thumbnails, metadata translations, filters, validation, and post assignment.
|
||||
- Template and Lua script management using a custom Ropey/Syntect/Cosmic Text editor and the documented, bDS2-signature-compatible project-scoped [`bds` host API](docs/scripting/API_REFERENCE.md) across utilities, rendered macros, and Blogmark transforms.
|
||||
- Template and Lua script management with explicit syntax-check feedback, using a custom Ropey/Syntect/Cosmic Text editor and the documented, bDS2-signature-compatible project-scoped [`bds` host API](docs/scripting/API_REFERENCE.md) across utilities, rendered macros, and Blogmark transforms.
|
||||
- SQLite and filesystem persistence with frontmatter, sidecars, rebuild, metadata diff/repair, and FTS5 search.
|
||||
- Markdown/Liquid rendering with native macros, multilingual routes, feeds, sitemap, Pagefind, and incremental site generation through cancellable section task groups.
|
||||
- Local preview in the app or system browser.
|
||||
|
||||
@@ -200,6 +200,7 @@ pub enum Message {
|
||||
ApplySiteValidation,
|
||||
EngineTaskDone {
|
||||
task_id: TaskId,
|
||||
operation: &'static str,
|
||||
label: String,
|
||||
result: Result<String, String>,
|
||||
},
|
||||
@@ -6299,11 +6300,12 @@ mod tests {
|
||||
save_template_editor_state_impl,
|
||||
};
|
||||
use crate::i18n::t;
|
||||
use crate::state::ToastLevel;
|
||||
use crate::state::sidebar_filter::{MediaFilter, PostFilter};
|
||||
use crate::views::media_editor::{MediaEditorMsg, MediaEditorState};
|
||||
use crate::views::modal;
|
||||
use crate::views::post_editor::{PostEditorMsg, PostEditorState};
|
||||
use crate::views::script_editor::ScriptEditorState;
|
||||
use crate::views::script_editor::{ScriptEditorMsg, ScriptEditorState};
|
||||
use crate::views::settings_view::SettingsViewState;
|
||||
use crate::views::template_editor::TemplateEditorState;
|
||||
use bds_core::db::Database;
|
||||
@@ -7704,6 +7706,66 @@ mod tests {
|
||||
assert!(editor.content.contains("new script"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn script_syntax_check_reports_success_and_failure() {
|
||||
let (db, project, tmp) = setup();
|
||||
let mut app = make_app(db, project, &tmp);
|
||||
let _ = app.update(Message::CreateScript);
|
||||
|
||||
let _ = app.update(Message::ScriptEditor(ScriptEditorMsg::CheckSyntax));
|
||||
assert_eq!(
|
||||
app.toasts.last().map(|toast| toast.message.as_str()),
|
||||
Some(t(UiLocale::En, "editor.syntaxValid").as_str())
|
||||
);
|
||||
|
||||
let _ = app.update(Message::ScriptEditor(ScriptEditorMsg::ContentChanged(
|
||||
"function render(".to_string(),
|
||||
)));
|
||||
let _ = app.update(Message::ScriptEditor(ScriptEditorMsg::CheckSyntax));
|
||||
assert_eq!(
|
||||
app.toasts.last().map(|toast| toast.level),
|
||||
Some(ToastLevel::Error)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rebuild_completion_refreshes_project_metadata_in_settings() {
|
||||
let (db, project, tmp) = setup();
|
||||
let mut app = make_app(db, project, &tmp);
|
||||
app.settings_state = Some(app.hydrate_settings_state());
|
||||
|
||||
let mut rebuilt = bds_core::db::queries::project::get_project_by_id(
|
||||
app.db.as_ref().unwrap().conn(),
|
||||
&app.active_project.as_ref().unwrap().id,
|
||||
)
|
||||
.unwrap();
|
||||
rebuilt.description = Some("Description from project.json".to_string());
|
||||
bds_core::db::queries::project::update_project(app.db.as_ref().unwrap().conn(), &rebuilt)
|
||||
.unwrap();
|
||||
|
||||
let label = t(UiLocale::En, "engine.rebuildStarted");
|
||||
let task_id = app.task_manager.submit(&label);
|
||||
let _ = app.update(Message::EngineTaskDone {
|
||||
task_id,
|
||||
operation: "engine.rebuildStarted",
|
||||
label,
|
||||
result: Ok("done".to_string()),
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
app.active_project
|
||||
.as_ref()
|
||||
.and_then(|project| project.description.as_deref()),
|
||||
Some("Description from project.json")
|
||||
);
|
||||
assert_eq!(
|
||||
app.settings_state
|
||||
.as_ref()
|
||||
.map(|state| state.project_description.text()),
|
||||
Some("Description from project.json\n".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_template_opens_editor_and_refreshes_sidebar() {
|
||||
let (db, project, tmp) = setup();
|
||||
|
||||
@@ -647,6 +647,7 @@ impl BdsApp {
|
||||
|
||||
pub(super) fn handle_script_editor_msg(&mut self, msg: ScriptEditorMsg) -> Task<Message> {
|
||||
let mut run_request = None;
|
||||
let mut syntax_notification = None;
|
||||
if let Some(tab_id) = self.active_tab.clone()
|
||||
&& let Some(state) = self.script_editors.get_mut(&tab_id)
|
||||
{
|
||||
@@ -680,14 +681,20 @@ impl BdsApp {
|
||||
return self.save_script_editor(&tab_id);
|
||||
}
|
||||
ScriptEditorMsg::CheckSyntax => {
|
||||
if let Some(st) = self.script_editors.get_mut(&tab_id) {
|
||||
match engine::script::validate_script_syntax(&st.content) {
|
||||
Ok(()) => {
|
||||
st.validation_error = None;
|
||||
}
|
||||
Err(e) => {
|
||||
st.validation_error = Some(e);
|
||||
}
|
||||
match engine::script::validate_script_syntax(&state.content) {
|
||||
Ok(()) => {
|
||||
state.validation_error = None;
|
||||
syntax_notification = Some((
|
||||
ToastLevel::Success,
|
||||
t(self.ui_locale, "editor.syntaxValid"),
|
||||
));
|
||||
}
|
||||
Err(error) => {
|
||||
state.validation_error = Some(error.clone());
|
||||
syntax_notification = Some((
|
||||
ToastLevel::Error,
|
||||
tw(self.ui_locale, "editor.syntaxInvalid", &[("error", &error)]),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -713,6 +720,9 @@ impl BdsApp {
|
||||
tab.is_dirty = st.is_dirty;
|
||||
}
|
||||
}
|
||||
if let Some((level, message)) = syntax_notification {
|
||||
self.notify(level, &message);
|
||||
}
|
||||
if let Some((source, entrypoint, kind)) = run_request {
|
||||
let offline_mode = self.offline_mode;
|
||||
let app_handler = crate::platform::script_host::handler(
|
||||
|
||||
@@ -213,6 +213,7 @@ impl BdsApp {
|
||||
Message::ApplySiteValidation => self.apply_site_validation(),
|
||||
Message::EngineTaskDone {
|
||||
task_id,
|
||||
operation,
|
||||
label,
|
||||
result,
|
||||
} => {
|
||||
@@ -222,6 +223,26 @@ impl BdsApp {
|
||||
_ if cancelled => {}
|
||||
Ok(detail) => {
|
||||
self.task_manager.complete(task_id);
|
||||
if operation == "engine.rebuildStarted" {
|
||||
let refreshed = self.db.as_ref().and_then(|db| {
|
||||
let id = &self.active_project.as_ref()?.id;
|
||||
bds_core::db::queries::project::get_project_by_id(db.conn(), id)
|
||||
.ok()
|
||||
});
|
||||
if let Some(project) = refreshed {
|
||||
if let Some(cached) = self
|
||||
.projects
|
||||
.iter_mut()
|
||||
.find(|cached| cached.id == project.id)
|
||||
{
|
||||
*cached = project.clone();
|
||||
}
|
||||
self.active_project = Some(project);
|
||||
if self.settings_state.is_some() {
|
||||
self.settings_state = Some(self.hydrate_settings_state());
|
||||
}
|
||||
}
|
||||
}
|
||||
self.notify(ToastLevel::Success, &format!("{label}: {detail}"));
|
||||
}
|
||||
Err(err) => {
|
||||
|
||||
@@ -314,6 +314,7 @@ impl BdsApp {
|
||||
},
|
||||
move |result| Message::EngineTaskDone {
|
||||
task_id,
|
||||
operation: "engine.reindexStarted",
|
||||
label: label_for_message.clone(),
|
||||
result,
|
||||
},
|
||||
@@ -321,7 +322,7 @@ impl BdsApp {
|
||||
}
|
||||
|
||||
/// Spawn a blocking engine operation on a background thread via TaskManager.
|
||||
pub(super) fn spawn_engine_task<F>(&mut self, label_key: &str, work: F) -> Task<Message>
|
||||
pub(super) fn spawn_engine_task<F>(&mut self, label_key: &'static str, work: F) -> Task<Message>
|
||||
where
|
||||
F: FnOnce(PathBuf, String, PathBuf, Arc<TaskManager>, TaskId) -> Result<String, String>
|
||||
+ Send
|
||||
@@ -332,7 +333,7 @@ impl BdsApp {
|
||||
|
||||
pub(super) fn spawn_grouped_engine_task<F>(
|
||||
&mut self,
|
||||
label_key: &str,
|
||||
label_key: &'static str,
|
||||
group_name: &str,
|
||||
work: F,
|
||||
) -> Task<Message>
|
||||
@@ -346,7 +347,7 @@ impl BdsApp {
|
||||
|
||||
pub(super) fn spawn_engine_task_in_group<F>(
|
||||
&mut self,
|
||||
label_key: &str,
|
||||
label_key: &'static str,
|
||||
group_name: Option<&str>,
|
||||
work: F,
|
||||
) -> Task<Message>
|
||||
@@ -391,6 +392,7 @@ impl BdsApp {
|
||||
},
|
||||
move |result| Message::EngineTaskDone {
|
||||
task_id,
|
||||
operation: label_key,
|
||||
label: label_for_msg.clone(),
|
||||
result,
|
||||
},
|
||||
|
||||
@@ -274,6 +274,8 @@ editor-discard = Verwerfen
|
||||
editor-validate = Validieren
|
||||
editor-run = Ausführen
|
||||
editor-checkSyntax = Syntax prüfen
|
||||
editor-syntaxValid = Syntax ist gültig.
|
||||
editor-syntaxInvalid = Syntaxfehler: { $error }
|
||||
editor-kind = Art
|
||||
editor-enabled = Aktiviert
|
||||
editor-entrypoint = Einstiegspunkt
|
||||
|
||||
@@ -265,6 +265,8 @@ editor-discard = Discard
|
||||
editor-validate = Validate
|
||||
editor-run = Run
|
||||
editor-checkSyntax = Check Syntax
|
||||
editor-syntaxValid = Syntax is valid.
|
||||
editor-syntaxInvalid = Syntax error: { $error }
|
||||
editor-kind = Kind
|
||||
editor-enabled = Enabled
|
||||
editor-entrypoint = Entrypoint
|
||||
|
||||
@@ -274,6 +274,8 @@ editor-discard = Descartar
|
||||
editor-validate = Validar
|
||||
editor-run = Ejecutar
|
||||
editor-checkSyntax = Verificar sintaxis
|
||||
editor-syntaxValid = La sintaxis es válida.
|
||||
editor-syntaxInvalid = Error de sintaxis: { $error }
|
||||
editor-kind = Tipo
|
||||
editor-enabled = Habilitado
|
||||
editor-entrypoint = Punto de entrada
|
||||
|
||||
@@ -274,6 +274,8 @@ editor-discard = Annuler les modifications
|
||||
editor-validate = Valider
|
||||
editor-run = Exécuter
|
||||
editor-checkSyntax = Vérifier la syntaxe
|
||||
editor-syntaxValid = La syntaxe est valide.
|
||||
editor-syntaxInvalid = Erreur de syntaxe : { $error }
|
||||
editor-kind = Type
|
||||
editor-enabled = Activé
|
||||
editor-entrypoint = Point d'entrée
|
||||
|
||||
@@ -274,6 +274,8 @@ editor-discard = Scarta
|
||||
editor-validate = Valida
|
||||
editor-run = Esegui
|
||||
editor-checkSyntax = Controlla sintassi
|
||||
editor-syntaxValid = La sintassi è valida.
|
||||
editor-syntaxInvalid = Errore di sintassi: { $error }
|
||||
editor-kind = Tipo
|
||||
editor-enabled = Abilitato
|
||||
editor-entrypoint = Punto di ingresso
|
||||
|
||||
Reference in New Issue
Block a user