diff --git a/examples/navbar-menus.rs b/examples/navbar-menus.rs index 8d330542..079508e9 100644 --- a/examples/navbar-menus.rs +++ b/examples/navbar-menus.rs @@ -10,7 +10,10 @@ impl Extension for SuperMenu { } fn initialize(&self) { - let home_path = |cx: &Context| join!("/lang/", cx.langid().language.as_str()).into(); + let home_path = |cx: &Context| match cx.langid().language.as_str() { + "en" => "/en", + _ => "/", + }; let navbar_menu = Navbar::brand_left(navbar::Brand::new().with_path(Some(home_path))) .with_expand(BreakPoint::LG) @@ -22,7 +25,7 @@ impl Extension for SuperMenu { )) .add_item(nav::Item::link_blank( L10n::l("sample_menus_item_blank"), - |_| "https://docs.rs/pagetop".into(), + |_| "https://docs.rs/pagetop", )) .add_item(nav::Item::dropdown( Dropdown::new() @@ -30,28 +33,28 @@ impl Extension for SuperMenu { .add_item(dropdown::Item::header(L10n::l("sample_menus_dev_header"))) .add_item(dropdown::Item::link( L10n::l("sample_menus_dev_getting_started"), - |_| "/dev/getting-started".into(), + |_| "/dev/getting-started", )) .add_item(dropdown::Item::link( L10n::l("sample_menus_dev_guides"), - |_| "/dev/guides".into(), + |_| "/dev/guides", )) .add_item(dropdown::Item::link_blank( L10n::l("sample_menus_dev_forum"), - |_| "https://forum.example.dev".into(), + |_| "https://forum.example.dev", )) .add_item(dropdown::Item::divider()) .add_item(dropdown::Item::header(L10n::l("sample_menus_sdk_header"))) .add_item(dropdown::Item::link( L10n::l("sample_menus_sdk_rust"), - |_| "/dev/sdks/rust".into(), + |_| "/dev/sdks/rust", )) .add_item(dropdown::Item::link(L10n::l("sample_menus_sdk_js"), |_| { - "/dev/sdks/js".into() + "/dev/sdks/js" })) .add_item(dropdown::Item::link( L10n::l("sample_menus_sdk_python"), - |_| "/dev/sdks/python".into(), + |_| "/dev/sdks/python", )) .add_item(dropdown::Item::divider()) .add_item(dropdown::Item::header(L10n::l( @@ -59,22 +62,22 @@ impl Extension for SuperMenu { ))) .add_item(dropdown::Item::link( L10n::l("sample_menus_plugin_auth"), - |_| "/dev/sdks/rust/plugins/auth".into(), + |_| "/dev/sdks/rust/plugins/auth", )) .add_item(dropdown::Item::link( L10n::l("sample_menus_plugin_cache"), - |_| "/dev/sdks/rust/plugins/cache".into(), + |_| "/dev/sdks/rust/plugins/cache", )) .add_item(dropdown::Item::divider()) .add_item(dropdown::Item::label(L10n::l("sample_menus_item_label"))) .add_item(dropdown::Item::link_disabled( L10n::l("sample_menus_item_disabled"), - |_| "#".into(), + |_| "#", )), )) .add_item(nav::Item::link_disabled( L10n::l("sample_menus_item_disabled"), - |_| "#".into(), + |_| "#", )), )) .add_item(navbar::Item::nav( @@ -85,10 +88,10 @@ impl Extension for SuperMenu { ) .add_item(nav::Item::link( L10n::l("sample_menus_item_sign_up"), - |_| "/auth/sign-up".into(), + |_| "/auth/sign-up", )) .add_item(nav::Item::link(L10n::l("sample_menus_item_login"), |_| { - "/auth/login".into() + "/auth/login" })), )); diff --git a/extensions/pagetop-bootsier/src/theme/dropdown/item.rs b/extensions/pagetop-bootsier/src/theme/dropdown/item.rs index 81f0ab08..1ff894e8 100644 --- a/extensions/pagetop-bootsier/src/theme/dropdown/item.rs +++ b/extensions/pagetop-bootsier/src/theme/dropdown/item.rs @@ -81,7 +81,7 @@ impl Component for Item { } => { let path = path(cx); let current_path = cx.request().map(|request| request.path()); - let is_current = !*disabled && (current_path == Some(&path)); + let is_current = !*disabled && (current_path == Some(path)); let mut classes = "dropdown-item".to_string(); if is_current { diff --git a/extensions/pagetop-bootsier/src/theme/nav/item.rs b/extensions/pagetop-bootsier/src/theme/nav/item.rs index 192f8df8..0aa30037 100644 --- a/extensions/pagetop-bootsier/src/theme/nav/item.rs +++ b/extensions/pagetop-bootsier/src/theme/nav/item.rs @@ -25,9 +25,6 @@ pub enum ItemKind { blank: bool, disabled: bool, }, - /// Contenido HTML arbitrario. El componente [`Html`] se renderiza tal cual como elemento del - /// menú, sin añadir ningún comportamiento de navegación adicional. - Html(Typed), /// Elemento que despliega un menú [`Dropdown`]. Dropdown(Typed), } @@ -118,7 +115,7 @@ impl Component for Item { } => { let path = path(cx); let current_path = cx.request().map(|request| request.path()); - let is_current = !*disabled && (current_path == Some(&path)); + let is_current = !*disabled && (current_path == Some(path)); let mut classes = "nav-link".to_string(); if is_current { @@ -151,12 +148,6 @@ impl Component for Item { }) } - ItemKind::Html(html) => PrepareMarkup::With(html! { - li id=[self.id()] class=[self.classes().get()] { - (html.render(cx)) - } - }), - ItemKind::Dropdown(menu) => { if let Some(dd) = menu.borrow() { let items = dd.items().render(cx); @@ -253,17 +244,6 @@ impl Item { } } - /// Crea un elemento con contenido HTML arbitrario. - /// - /// El contenido se renderiza tal cual lo devuelve el componente [`Html`], dentro de un `
  • ` - /// con las clases de navegación asociadas a [`Item`]. - pub fn html(html: Html) -> Self { - Item { - item_kind: ItemKind::Html(Typed::with(html)), - ..Default::default() - } - } - /// Crea un elemento de navegación que contiene un menú desplegable [`Dropdown`]. /// /// Sólo se tienen en cuenta **el título** (si no existe le asigna uno por defecto) y **la lista diff --git a/extensions/pagetop-bootsier/src/theme/navbar/brand.rs b/extensions/pagetop-bootsier/src/theme/navbar/brand.rs index 2fc31ef7..eb2d17a8 100644 --- a/extensions/pagetop-bootsier/src/theme/navbar/brand.rs +++ b/extensions/pagetop-bootsier/src/theme/navbar/brand.rs @@ -23,7 +23,7 @@ pub struct Brand { /// Devuelve el eslogan de la marca. slogan: L10n, /// Devuelve la función que resuelve la URL asociada a la marca (si existe). - #[default(_code = "Some(|_| \"/\".into())")] + #[default(_code = "Some(|_| \"/\")")] path: Option, } diff --git a/src/base/component/intro.rs b/src/base/component/intro.rs index 1a7c440b..64d2dddd 100644 --- a/src/base/component/intro.rs +++ b/src/base/component/intro.rs @@ -91,14 +91,13 @@ pub struct Intro { } impl Default for Intro { + #[rustfmt::skip] fn default() -> Self { - const BUTTON_LINK: &str = "https://pagetop.cillero.es"; - Intro { - title: L10n::l("intro_default_title"), - slogan: L10n::l("intro_default_slogan").with_arg("app", &global::SETTINGS.app.name), - button: Some((L10n::l("intro_default_button"), |_| BUTTON_LINK.into())), - opening: IntroOpening::default(), + title : L10n::l("intro_default_title"), + slogan : L10n::l("intro_default_slogan").with_arg("app", &global::SETTINGS.app.name), + button : Some((L10n::l("intro_default_button"), |_| "https://pagetop.cillero.es")), + opening : IntroOpening::default(), children: Children::default(), } } diff --git a/src/base/extension/welcome.rs b/src/base/extension/welcome.rs index d4a78547..b875163b 100644 --- a/src/base/extension/welcome.rs +++ b/src/base/extension/welcome.rs @@ -1,15 +1,10 @@ use crate::prelude::*; -/// Página de bienvenida de PageTop. +/// Página de bienvenida predeterminada de PageTop. /// -/// Esta extensión se instala por defecto si el ajuste de configuración [`global::App::welcome`] es -/// `true`. Muestra una página de bienvenida de PageTop en la ruta raíz (`/`) o en `/lang/{lang}`, -/// siempre que `{lang}` sea un idioma soportado (si no, devuelve una página de error 404). -/// -/// No obstante, cualquier extensión puede sobrescribir este comportamiento si utiliza estas mismas -/// rutas. -/// -/// Resulta útil en demos o para comprobar rápidamente que el servidor ha arrancado correctamente. +/// Esta extensión se instala por defecto y muestra una página en la ruta raíz (`/`) cuando no se ha +/// configurado ninguna página de inicio personalizada. Permite confirmar que el servidor está +/// funcionando correctamente. pub struct Welcome; impl Extension for Welcome { @@ -22,33 +17,15 @@ impl Extension for Welcome { } fn configure_service(&self, scfg: &mut service::web::ServiceConfig) { - scfg.route("/", service::web::get().to(home_page)) - .route("/lang/{lang}", service::web::get().to(home_lang)); + scfg.route("/", service::web::get().to(homepage)); } } -async fn home_page(request: HttpRequest) -> ResultPage { - let language = LangMatch::from_request(Some(&request)); - home(request, &language) -} - -async fn home_lang( - request: HttpRequest, - path: service::web::Path, -) -> ResultPage { - let language = LangMatch::resolve(path.into_inner()); - match language { - LangMatch::Found(_) => home(request, &language), - _ => Err(ErrorPage::NotFound(request)), - } -} - -fn home(request: HttpRequest, language: &impl LangId) -> ResultPage { +async fn homepage(request: HttpRequest) -> ResultPage { let app = &global::SETTINGS.app.name; Page::new(request) .with_title(L10n::l("welcome_title")) - .with_langid(language) .add_child( Intro::new() .add_child( diff --git a/src/config.rs b/src/config.rs index 32a73083..1607a874 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,4 +1,4 @@ -//! Carga las opciones de configuración de la aplicación. +//! Carga las opciones de configuración. //! //! Estos ajustes se obtienen de archivos [TOML](https://toml.io) como pares `clave = valor` que se //! mapean a estructuras **fuertemente tipadas** y valores predefinidos. @@ -125,7 +125,7 @@ const DEFAULT_CONFIG_DIR: &str = "config"; // Modo de ejecución por defecto. const DEFAULT_RUN_MODE: &str = "default"; -/// Valores originales de los archivos de configuración como pares `clave = valor`. +/// Valores originales cargados desde los archivos de configuración como pares `clave = valor`. pub static CONFIG_VALUES: LazyLock> = LazyLock::new(|| { // CONFIG_DIR (si existe) o DEFAULT_CONFIG_DIR. Si no se puede resolver, se usa tal cual. let dir = env::var_os("CONFIG_DIR").unwrap_or_else(|| DEFAULT_CONFIG_DIR.into()); @@ -229,11 +229,10 @@ pub static CONFIG_VALUES: LazyLock> = LazyLock::new( macro_rules! include_config { ( $SETTINGS_NAME:ident : $Settings_Type:ty => [ $( $k:literal => $v:expr ),* $(,)? ] ) => { #[doc = concat!( - "Ajustes de configuración y **valores por defecto** para ", - "[`", stringify!($Settings_Type), "`]." + "Instancia los ajustes de configuración para [`", stringify!($Settings_Type), "`]." )] #[doc = ""] - #[doc = "Valores predeterminados que se aplican en ausencia de configuración:"] + #[doc = "Valores por defecto:"] #[doc = "```text"] $( #[doc = concat!($k, " = ", stringify!($v))] diff --git a/src/core/component.rs b/src/core/component.rs index b905a495..9c9ade2e 100644 --- a/src/core/component.rs +++ b/src/core/component.rs @@ -1,7 +1,5 @@ //! API para construir nuevos componentes. -use std::borrow::Cow; - mod definition; pub use definition::{Component, ComponentRender}; @@ -68,10 +66,6 @@ pub type FnIsRenderable = fn(cx: &Context) -> bool; /// Alias de función (*callback*) para **resolver una URL** según el contexto de renderizado. /// -/// Se usa para generar enlaces dinámicos en función del contexto (petición, idioma, etc.). El -/// resultado se devuelve como [`Cow<'static, str>`](std::borrow::Cow), lo que permite: -/// -/// - Usar rutas estáticas sin asignaciones adicionales (`"/path".into()`). -/// - Construir rutas dinámicas en tiempo de ejecución (`format!(...).into()`), por ejemplo, en -/// función de parámetros almacenados en [`Context`]. -pub type FnPathByContext = fn(cx: &Context) -> Cow<'static, str>; +/// Se usa para generar enlaces dinámicos en función del contexto (petición, idioma, etc.). Debe +/// devolver una referencia válida durante el renderizado. +pub type FnPathByContext = fn(cx: &Context) -> &str; diff --git a/src/core/extension/all.rs b/src/core/extension/all.rs index b787c9a8..fa67671d 100644 --- a/src/core/extension/all.rs +++ b/src/core/extension/all.rs @@ -29,10 +29,8 @@ pub fn register_extensions(root_extension: Option) { add_to_enabled(&mut enabled_list, extension); } - // Añade la página de bienvenida predefinida si se habilita en la configuración. - if global::SETTINGS.app.welcome { - add_to_enabled(&mut enabled_list, &crate::base::extension::Welcome); - } + // Añade la página de bienvenida por defecto a la lista de extensiones habilitadas. + add_to_enabled(&mut enabled_list, &crate::base::extension::Welcome); // Guarda la lista final de extensiones habilitadas. ENABLED_EXTENSIONS.write().append(&mut enabled_list); diff --git a/src/global.rs b/src/global.rs index 6726a3ef..ee07d818 100644 --- a/src/global.rs +++ b/src/global.rs @@ -11,7 +11,6 @@ include_config!(SETTINGS: Settings => [ "app.theme" => "Basic", "app.language" => "", "app.startup_banner" => "Slant", - "app.welcome" => true, // [dev] "dev.pagetop_static_dir" => "", @@ -60,19 +59,13 @@ pub struct App { /// Banner ASCII mostrado al inicio: *"Off"* (desactivado), *"Slant"*, *"Small"*, *"Speed"* o /// *"Starwars"*. pub startup_banner: String, - /// Activa la página de bienvenida de PageTop. - /// - /// Si está activada, se instala la extensión [`Welcome`](crate::base::extension::Welcome), que - /// ofrece una página de bienvenida predefinida en `"/"` y también en `"/lang/{lang}"`, para - /// mostrar el contenido en el idioma `{lang}`, siempre que esté soportado. - pub welcome: bool, /// Modo de ejecución, dado por la variable de entorno `PAGETOP_RUN_MODE`, o *"default"* si no /// está definido. pub run_mode: String, } #[derive(Debug, Deserialize)] -/// Sección `[dev]` de la configuración. Forma parte de [`Settings`]. +/// Sección `[Dev]` de la configuración. Forma parte de [`Settings`]. pub struct Dev { /// Directorio desde el que servir los archivos estáticos de PageTop. /// @@ -94,7 +87,7 @@ pub struct Log { /// *"Warn"*, *"Info"*, *"Debug"* o *"Trace"*. /// Ejemplo: "Error,actix_server::builder=Info,tracing_actix_web=Debug". pub tracing: String, - /// Muestra los mensajes de traza en el terminal (*"Stdout"*) o los vuelca en archivos con + /// Muestra los mensajes de traza en el terminal (*"Stdout"*) o las registra en archivos con /// rotación: *"Daily"*, *"Hourly"*, *"Minutely"* o *"Endless"*. pub rolling: String, /// Directorio para los archivos de traza (si `rolling` ≠ *"Stdout"*).