From dbf0894894212a5ff996616e7539a94fcf53e67d Mon Sep 17 00:00:00 2001 From: Manuel Cillero Date: Sat, 20 Jun 2026 19:47:19 +0200 Subject: [PATCH 1/2] =?UTF-8?q?=E2=9C=A8=20(bootsier):=20A=C3=B1ade=20ajus?= =?UTF-8?q?te=20dev.bootsier=5Fstatic=5Fdir?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Permite servir `css`, `js` y `fonts` desde disco mientras la aplicación está en ejecución, sin necesidad de recompilar. --- extensions/pagetop-bootsier/src/config.rs | 18 +++++++++++++++++- extensions/pagetop-bootsier/src/lib.rs | 13 +++++++++++-- src/global.rs | 9 ++++----- 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/extensions/pagetop-bootsier/src/config.rs b/extensions/pagetop-bootsier/src/config.rs index 14ee27e1..dede3c4c 100644 --- a/extensions/pagetop-bootsier/src/config.rs +++ b/extensions/pagetop-bootsier/src/config.rs @@ -9,7 +9,7 @@ //! //! Uso: //! -//! ```rust +//! ```rust,no_run //! # use pagetop::prelude::*; //! use pagetop_bootsier::config; //! @@ -26,12 +26,15 @@ use serde::Deserialize; include_config!(SETTINGS: Settings => [ // [bootsier] "bootsier.max_width" => "1440px", + // [dev] + "dev.bootsier_static_dir" => "", ]); /// Ajustes para la sección [`Bootsier`] de [`SETTINGS`]. #[derive(Debug, Deserialize)] pub struct Settings { pub bootsier: Bootsier, + pub dev: Dev, } /// Sección **`[bootsier]`** de la configuración. Forma parte de [`Settings`]. @@ -40,3 +43,16 @@ pub struct Bootsier { /// Ancho máximo predeterminado para la página, por ejemplo "100%" o "90rem". pub max_width: UnitValue, } + +/// Sección **`[dev]`** de la configuración. Forma parte de [`Settings`]. +#[derive(Debug, Deserialize)] +pub struct Dev { + /// Directorio raíz de `static/` para servir los archivos estáticos propios de Bootsier. + /// + /// Si se indica una ruta válida, absoluta o relativa al directorio del proyecto o del binario + /// en ejecución, los archivos estáticos se servirán desde disco. Útil para poder modificar los + /// archivos estáticos mientras la aplicación está en ejecución, sin necesidad de recompilar. + /// + /// Si la cadena está vacía, se ignora este ajuste. + pub bootsier_static_dir: String, +} diff --git a/extensions/pagetop-bootsier/src/lib.rs b/extensions/pagetop-bootsier/src/lib.rs index d0820937..2ce4f566 100644 --- a/extensions/pagetop-bootsier/src/lib.rs +++ b/extensions/pagetop-bootsier/src/lib.rs @@ -134,8 +134,17 @@ impl Extension for Bootsier { } fn configure_router(&self, router: Router) -> Router { - serve_static_files!(router, [bootsier_bs] => "/bootsier/bs"); - serve_static_files!(router, [bootsier_js] => "/bootsier/js"); + let base = &config::SETTINGS.dev.bootsier_static_dir; + let subdir = |s: &str| { + if base.is_empty() { + String::new() + } else { + format!("{base}/{s}") + } + }; + serve_static_files!(router, [subdir("css"), bootsier_css] => "/bootsier/css"); + serve_static_files!(router, [subdir("js"), bootsier_js] => "/bootsier/js"); + serve_static_files!(router, [subdir("fonts"), bootsier_fonts] => "/bootsier/fonts"); router } } diff --git a/src/global.rs b/src/global.rs index 2d7b1b60..17bbbe94 100644 --- a/src/global.rs +++ b/src/global.rs @@ -90,12 +90,11 @@ pub struct App { /// Sección **`[dev]`** de la configuración. Forma parte de [`Settings`]. #[derive(Debug, Deserialize)] pub struct Dev { - /// Directorio desde el que servir los archivos estáticos de PageTop. + /// Directorio raíz de `static/` para servir los archivos estáticos propios de PageTop. /// - /// Por defecto, los archivos se integran en el binario de la aplicación. Si aquí se indica una - /// ruta válida, ya sea absoluta o relativa al directorio del proyecto o del binario en - /// ejecución, se servirán desde el sistema de ficheros en su lugar. Esto es especialmente útil - /// en desarrollo, ya que evita recompilar el proyecto por cambios en estos archivos. + /// Si se indica una ruta válida, absoluta o relativa al directorio del proyecto o del binario + /// en ejecución, los archivos estáticos se servirán desde disco. Útil para poder modificar los + /// archivos estáticos mientras la aplicación está en ejecución, sin necesidad de recompilar. /// /// Si la cadena está vacía, se ignora este ajuste. pub pagetop_static_dir: String, From d495c05b965c7090a1e7a9360b6e6684aa47173a Mon Sep 17 00:00:00 2001 From: Manuel Cillero Date: Sat, 20 Jun 2026 19:52:21 +0200 Subject: [PATCH 2/2] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20(bootsier):=20Reorgani?= =?UTF-8?q?za=20activos=20est=C3=A1ticos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Compila SCSS, copia JS y fuentes desde `assets/` con prefijo bootsier. --- extensions/pagetop-bootsier/build.rs | 85 +++++++++++++++++++++++--- extensions/pagetop-bootsier/src/lib.rs | 13 ++-- 2 files changed, 84 insertions(+), 14 deletions(-) diff --git a/extensions/pagetop-bootsier/build.rs b/extensions/pagetop-bootsier/build.rs index df7a2750..8de7c387 100644 --- a/extensions/pagetop-bootsier/build.rs +++ b/extensions/pagetop-bootsier/build.rs @@ -1,20 +1,85 @@ -use pagetop_build::StaticFilesBundle; +//! Script de compilacion de activos estaticos. +//! +//! Genera el directorio `static/` a partir de `assets/` y embebe su contenido en el binario: +//! +//! - `static/css/` - CSS compilado a partir de los archivos SCSS de `assets/`. +//! - `static/js/` - JS copiado desde `assets/`, renombrando AdminLTE a `bootsier.min.js`. +//! - `static/fonts/` - Fuentes copiadas desde `assets/`. +//! +//! Los archivos `.map` se copian a `static/js/` para uso en desarrollo pero no se incluyen en el +//! binario embebido. + +use pagetop_build::{StaticFilesBundle, compile_scss, copy_file, copy_file_replacing, minify_js}; -use std::env; use std::path::Path; fn main() -> std::io::Result<()> { - StaticFilesBundle::from_scss("./static/scss/bootsier.scss", "bootstrap.min.css") - .with_name("bootsier_bs") + // Regenera `static/` desde cero sólo si hay cambios en `assets/`. + println!("cargo:rerun-if-changed=assets"); + let _ = std::fs::remove_dir_all("static"); + + // CSS: Bootstrap 5.3.8 + AdminLTE 4.0.0 + Bootstrap Icons 1.13.1. + compile_scss("assets/bootsier.scss", "static/css/bootsier.min.css")?; + + // JS: Bootstrap bundle. + copy_file( + "assets/bootstrap-5.3.8/js/bootstrap.bundle.min.js", + "static/js/bootsier.bundle.min.js", + )?; + copy_file( + "assets/bootstrap-5.3.8/js/bootstrap.bundle.min.js.map", + "static/js/bootsier.bundle.min.js.map", + )?; + // JS: AdminLTE renombrado a bootsier.extended.min.js. + copy_file_replacing( + "assets/adminlte-4.0.0/js/adminlte.min.js", + "static/js/bootsier.extended.min.js", + &[("adminlte.min.js.map", "bootsier.extended.min.js.map")], + )?; + copy_file( + "assets/adminlte-4.0.0/js/adminlte.min.js.map", + "static/js/bootsier.extended.min.js.map", + )?; + // JS: shell de Bootsier. + minify_js( + "assets/bootsier.shell.js", + "static/js/bootsier.shell.min.js", + )?; + + // Fuentes: Bootstrap Icons. + copy_file( + "assets/bootstrap-icons-1.13.1/fonts/bootstrap-icons.woff2", + "static/fonts/bootsier.icons.woff2", + )?; + copy_file( + "assets/bootstrap-icons-1.13.1/fonts/bootstrap-icons.woff", + "static/fonts/bootsier.icons.woff", + )?; + // Fuentes: Source Sans 3 (SIL OFL 1.1). + copy_file( + "assets/adminlte-4.0.0/fonts/SourceSans3VF-Upright.otf.woff2", + "static/fonts/bootsier.font.woff2", + )?; + copy_file( + "assets/adminlte-4.0.0/fonts/SourceSans3VF-Italic.otf.woff2", + "static/fonts/bootsier.font.italic.woff2", + )?; + + // Preparación de los paquetes para embeber en el binario. + StaticFilesBundle::from_dir("./static/css", None) + .with_name("bootsier_css") .build()?; - StaticFilesBundle::from_dir("./static/js", Some(bootstrap_js_files)) + + StaticFilesBundle::from_dir("./static/js", Some(only_js_files)) .with_name("bootsier_js") + .build()?; + + StaticFilesBundle::from_dir("./static/fonts", None) + .with_name("bootsier_fonts") .build() } -fn bootstrap_js_files(path: &Path) -> bool { - let bootstrap_js = "bootstrap.bundle.min.js"; - // No filtra durante el desarrollo, solo en la compilación "release". - env::var("PROFILE").unwrap_or_else(|_| "release".to_string()) != "release" - || path.file_name().is_some_and(|f| f == bootstrap_js) +// Los archivos .map no se embeben en el binario; solo se sirven desde disco en desarrollo. +fn only_js_files(path: &Path) -> bool { + path.extension().map_or(false, |ext| ext == "js") } diff --git a/extensions/pagetop-bootsier/src/lib.rs b/extensions/pagetop-bootsier/src/lib.rs index 2ce4f566..bb7c09e0 100644 --- a/extensions/pagetop-bootsier/src/lib.rs +++ b/extensions/pagetop-bootsier/src/lib.rs @@ -157,14 +157,19 @@ impl Theme for Bootsier { fn before_render_page_body(&self, page: &mut Page) { page.alter_assets(AssetsOp::AddStyleSheet( - StyleSheet::from("/bootsier/bs/bootstrap.min.css") + StyleSheet::from("/bootsier/css/bootsier.min.css") + .with_version(ADMINLTE_VERSION) + .with_weight(-90), + )) + .alter_assets(AssetsOp::AddJavaScript( + JavaScript::defer("/bootsier/js/bootsier.bundle.min.js") .with_version(BOOTSTRAP_VERSION) .with_weight(-90), )) .alter_assets(AssetsOp::AddJavaScript( - JavaScript::defer("/bootsier/js/bootstrap.bundle.min.js") - .with_version(BOOTSTRAP_VERSION) - .with_weight(-90), + JavaScript::defer("/bootsier/js/bootsier.extended.min.js") + .with_version(ADMINLTE_VERSION) + .with_weight(-89), )) .alter_child_in( &DefaultRegion::Footer,