diff --git a/crates/ironclaw_frontend/Cargo.toml b/crates/ironclaw_frontend/Cargo.toml
index b8e8aa15..d1344e00 100644
--- a/crates/ironclaw_frontend/Cargo.toml
+++ b/crates/ironclaw_frontend/Cargo.toml
@@ -2,8 +2,9 @@
name = "ironclaw_frontend"
version = "0.1.0"
edition = "2024"
-rust-version = "1.85"
+rust-version = "1.92"
description = "Frontend assets, layout configuration, and widget extension system for IronClaw"
+license = "MIT OR Apache-2.0"
[package.metadata.dist]
dist = false
diff --git a/crates/ironclaw_frontend/src/bundle.rs b/crates/ironclaw_frontend/src/bundle.rs
index 5e25e1a2..248d01a2 100644
--- a/crates/ironclaw_frontend/src/bundle.rs
+++ b/crates/ironclaw_frontend/src/bundle.rs
@@ -92,10 +92,7 @@ pub fn assemble_index(base_html: &str, bundle: &FrontendBundle) -> String {
// Custom CSS
if let Some(ref custom_css) = bundle.custom_css {
if !custom_css.trim().is_empty() {
- body_injections.push(format!(
- "",
- custom_css
- ));
+ body_injections.push(format!("", custom_css));
}
}
diff --git a/src/channels/web/handlers/frontend.rs b/src/channels/web/handlers/frontend.rs
index c9be34df..9c820583 100644
--- a/src/channels/web/handlers/frontend.rs
+++ b/src/channels/web/handlers/frontend.rs
@@ -77,7 +77,10 @@ pub async fn frontend_widgets_handler(
) -> Result>, (StatusCode, String)> {
let workspace = resolve_workspace(&state, &user).await?;
- let entries = workspace.list("frontend/widgets/").await.unwrap_or_default();
+ let entries = workspace
+ .list("frontend/widgets/")
+ .await
+ .unwrap_or_default();
let mut manifests = Vec::new();
for entry in entries {
@@ -86,8 +89,14 @@ pub async fn frontend_widgets_handler(
}
let manifest_path = format!("frontend/widgets/{}/manifest.json", entry.name());
if let Ok(doc) = workspace.read(&manifest_path).await {
- if let Ok(manifest) = serde_json::from_str::(&doc.content) {
- manifests.push(manifest);
+ match serde_json::from_str::(&doc.content) {
+ Ok(manifest) => manifests.push(manifest),
+ Err(e) => {
+ tracing::warn!(
+ path = %manifest_path,
+ "skipping widget with invalid manifest: {e}"
+ );
+ }
}
}
}
diff --git a/src/channels/web/handlers/mod.rs b/src/channels/web/handlers/mod.rs
index 16ca5673..eb8da487 100644
--- a/src/channels/web/handlers/mod.rs
+++ b/src/channels/web/handlers/mod.rs
@@ -16,9 +16,9 @@ pub mod users;
pub mod chat;
#[allow(dead_code)]
pub mod extensions;
+pub mod frontend;
#[allow(dead_code)]
pub mod settings;
#[allow(dead_code)]
pub mod static_files;
-pub mod frontend;
pub mod webhooks;
diff --git a/src/channels/web/server.rs b/src/channels/web/server.rs
index 32c50668..69b3efeb 100644
--- a/src/channels/web/server.rs
+++ b/src/channels/web/server.rs
@@ -33,6 +33,10 @@ use crate::channels::relay::DEFAULT_RELAY_NAME;
use crate::channels::web::auth::{
AuthenticatedUser, CombinedAuthState, UserIdentity, auth_middleware,
};
+use crate::channels::web::handlers::frontend::{
+ frontend_layout_handler, frontend_layout_update_handler, frontend_widget_file_handler,
+ frontend_widgets_handler,
+};
use crate::channels::web::handlers::jobs::{
job_files_list_handler, job_files_read_handler, jobs_cancel_handler, jobs_detail_handler,
jobs_events_handler, jobs_list_handler, jobs_prompt_handler, jobs_restart_handler,
@@ -46,10 +50,6 @@ use crate::channels::web::handlers::routines::{
routines_delete_handler, routines_detail_handler, routines_list_handler,
routines_summary_handler, routines_toggle_handler, routines_trigger_handler,
};
-use crate::channels::web::handlers::frontend::{
- frontend_layout_handler, frontend_layout_update_handler, frontend_widget_file_handler,
- frontend_widgets_handler,
-};
use crate::channels::web::handlers::skills::{
skills_install_handler, skills_list_handler, skills_remove_handler, skills_search_handler,
};