diff --git a/crates/oapi/src/rapidoc/mod.rs b/crates/oapi/src/rapidoc/mod.rs
index 289ba297e..69a2e746c 100644
--- a/crates/oapi/src/rapidoc/mod.rs
+++ b/crates/oapi/src/rapidoc/mod.rs
@@ -4,6 +4,8 @@
//!
//! [salvo]:
//!
+use std::borrow::Cow;
+
use salvo_core::writing::Text;
use salvo_core::{async_trait, Depot, FlowCtrl, Handler, Request, Response, Router};
@@ -28,15 +30,15 @@ const INDEX_TMPL: &str = r#"
#[derive(Clone, Debug)]
pub struct RapiDoc {
/// The title of the html page. The default title is "RapiDoc".
- pub title: String,
+ pub title: Cow<'static, str>,
/// The version of the html page.
- pub keywords: Option,
+ pub keywords: Option>,
/// The description of the html page.
- pub description: Option,
+ pub description: Option>,
/// The lib url path.
- pub lib_url: String,
+ pub lib_url: Cow<'static, str>,
/// The spec url path.
- pub spec_url: String,
+ pub spec_url: Cow<'static, str>,
}
impl RapiDoc {
/// Create a new [`RapiDoc`] for given path.
@@ -50,7 +52,7 @@ impl RapiDoc {
/// # use salvo_oapi::rapidoc::RapiDoc;
/// let doc = RapiDoc::new("/openapi.json");
/// ```
- pub fn new(spec_url: impl Into) -> Self {
+ pub fn new(spec_url: impl Into>) -> Self {
Self {
title: "RapiDoc".into(),
keywords: None,
@@ -61,25 +63,25 @@ impl RapiDoc {
}
/// Set title of the html page. The default title is "RapiDoc".
- pub fn title(mut self, title: impl Into) -> Self {
+ pub fn title(mut self, title: impl Into>) -> Self {
self.title = title.into();
self
}
/// Set keywords of the html page.
- pub fn keywords(mut self, keywords: impl Into) -> Self {
+ pub fn keywords(mut self, keywords: impl Into>) -> Self {
self.keywords = Some(keywords.into());
self
}
/// Set description of the html page.
- pub fn description(mut self, description: impl Into) -> Self {
+ pub fn description(mut self, description: impl Into>) -> Self {
self.description = Some(description.into());
self
}
/// Set the lib url path.
- pub fn lib_url(mut self, lib_url: impl Into) -> Self {
+ pub fn lib_url(mut self, lib_url: impl Into>) -> Self {
self.lib_url = lib_url.into();
self
}
diff --git a/crates/oapi/src/redoc/mod.rs b/crates/oapi/src/redoc/mod.rs
index 6b0dc3db0..7700a2430 100644
--- a/crates/oapi/src/redoc/mod.rs
+++ b/crates/oapi/src/redoc/mod.rs
@@ -4,6 +4,7 @@
//!
//! [salvo]:
//!
+use std::borrow::Cow;
use salvo_core::writing::Text;
use salvo_core::{async_trait, Depot, FlowCtrl, Handler, Request, Response, Router};
@@ -14,9 +15,9 @@ const INDEX_TMPL: &str = r#"
{{title}}
{{keywords}}
{{description}}
-
-
-
+
+
+
-
+ {{header}}
-
"#;
/// Implements [`Handler`] for serving Scalar.
@@ -37,15 +35,19 @@ const INDEX_TMPL: &str = r#"
#[derive(Clone, Debug)]
pub struct Scalar {
/// The title of the html page. The default title is "Scalar".
- pub title: String,
+ pub title: Cow<'static, str>,
/// The version of the html page.
- pub keywords: Option,
+ pub keywords: Option>,
/// The description of the html page.
- pub description: Option,
+ pub description: Option>,
+ /// Custom style for the html page.
+ pub style: Option>,
+ /// Custom header for the html page.
+ pub header: Option>,
/// The lib url path.
- pub lib_url: String,
+ pub lib_url: Cow<'static, str>,
/// The spec url path.
- pub spec_url: String,
+ pub spec_url: Cow<'static, str>,
}
impl Scalar {
/// Create a new [`Scalar`] for given path.
@@ -59,36 +61,38 @@ impl Scalar {
/// # use salvo_oapi::scalar::Scalar;
/// let doc = Scalar::new("/openapi.json");
/// ```
- pub fn new(spec_url: impl Into) -> Self {
+ pub fn new(spec_url: impl Into>) -> Self {
Self {
title: "Scalar".into(),
keywords: None,
description: None,
+ style: Some(Cow::from(DEFAULT_STYLE)),
+ header: None,
lib_url: "https://cdn.jsdelivr.net/npm/@scalar/api-reference".into(),
spec_url: spec_url.into(),
}
}
/// Set title of the html page. The default title is "Scalar".
- pub fn title(mut self, title: impl Into) -> Self {
+ pub fn title(mut self, title: impl Into>) -> Self {
self.title = title.into();
self
}
/// Set keywords of the html page.
- pub fn keywords(mut self, keywords: impl Into) -> Self {
+ pub fn keywords(mut self, keywords: impl Into>) -> Self {
self.keywords = Some(keywords.into());
self
}
/// Set description of the html page.
- pub fn description(mut self, description: impl Into) -> Self {
+ pub fn description(mut self, description: impl Into>) -> Self {
self.description = Some(description.into());
self
}
/// Set the lib url path.
- pub fn lib_url(mut self, lib_url: impl Into) -> Self {
+ pub fn lib_url(mut self, lib_url: impl Into>) -> Self {
self.lib_url = lib_url.into();
self
}
@@ -116,12 +120,131 @@ impl Handler for Scalar {
.as_ref()
.map(|s| format!("", s))
.unwrap_or_default();
+ let style = self
+ .style
+ .as_ref()
+ .map(|s| format!("", s))
+ .unwrap_or_default();
let html = INDEX_TMPL
.replacen("{{lib_url}}", &self.lib_url, 1)
.replacen("{{spec_url}}", &self.spec_url, 1)
.replacen("{{title}}", &self.title, 1)
.replacen("{{keywords}}", &keywords, 1)
- .replacen("{{description}}", &description, 1);
+ .replacen("{{description}}", &description, 1)
+ .replacen("{{style}}", &style, 1)
+ .replacen("{{header}}", self.header.as_deref().unwrap_or_default(), 1);
res.render(Text::Html(html));
}
}
+
+const DEFAULT_STYLE: &'static str = r#":root {
+ --theme-font: 'Inter', var(--system-fonts);
+ }
+ /* basic theme */
+ .light-mode {
+ --theme-color-1: #2c3d50;
+ --theme-color-2: #38495c;
+ --theme-color-3: #445569;
+ --theme-color-accent: #3faf7c;
+
+ --theme-background-1: #fff;
+ --theme-background-2: #f6f6f6;
+ --theme-background-3: #e7e7e7;
+ --theme-background-accent: #8ab4f81f;
+
+ --theme-border-color: rgba(0, 0, 0, 0.1);
+ }
+ .dark-mode {
+ --theme-color-1: rgb(150, 167, 183, 1);
+ --theme-color-2: rgba(150, 167, 183, 0.72);
+ --theme-color-3: rgba(150, 167, 183, 0.54);
+ --theme-color-accent: #329066;
+
+ --theme-background-1: #22272e;
+ --theme-background-2: #282c34;
+ --theme-background-3: #343841;
+ --theme-background-accent: #3290661f;
+
+ --theme-border-color: rgba(255, 255, 255, 0.1);
+ }
+ /* Document header */
+ .light-mode .t-doc__header {
+ --header-background-1: var(--theme-background-1);
+ --header-border-color: var(--theme-border-color);
+ --header-color-1: var(--theme-color-1);
+ --header-color-2: var(--theme-color-2);
+ --header-background-toggle: var(--theme-color-3);
+ --header-call-to-action-color: var(--theme-color-accent);
+ }
+
+ .dark-mode .t-doc__header {
+ --header-background-1: var(--theme-background-1);
+ --header-border-color: var(--theme-border-color);
+ --header-color-1: var(--theme-color-1);
+ --header-color-2: var(--theme-color-2);
+ --header-background-toggle: var(--theme-color-3);
+ --header-call-to-action-color: var(--theme-color-accent);
+ }
+ /* Document Sidebar */
+ .light-mode .t-doc__sidebar,
+ .dark-mode .t-doc__sidebar {
+ --sidebar-background-1: var(--theme-background-1);
+ --sidebar-item-hover-color: var(--theme-color-accent);
+ --sidebar-item-hover-background: transparent;
+ --sidebar-item-active-background: transparent;
+ --sidebar-border-color: var(--theme-border-color);
+ --sidebar-color-1: var(--theme-color-1);
+ --sidebar-color-2: var(--theme-color-2);
+ --sidebar-color-active: var(--theme-color-accent);
+ --sidebar-search-background: transparent;
+ --sidebar-search-border-color: var(--theme-border-color);
+ --sidebar-search--color: var(--theme-color-3);
+ }
+ .light-mode .t-doc__sidebar .active_page.sidebar-heading,
+ .dark-mode .t-doc__sidebar .active_page.sidebar-heading {
+ background: transparent !important;
+ box-shadow: inset 3px 0 0 var(--theme-color-accent);
+ }
+
+ /* advanced */
+ .light-mode {
+ --theme-button-1: rgb(49 53 56);
+ --theme-button-1-color: #fff;
+ --theme-button-1-hover: rgb(28 31 33);
+
+ --theme-color-green: #069061;
+ --theme-color-red: #ef0006;
+ --theme-color-yellow: #edbe20;
+ --theme-color-blue: #0082d0;
+ --theme-color-orange: #fb892c;
+ --theme-color-purple: #5203d1;
+
+ --theme-scrollbar-color: rgba(0, 0, 0, 0.18);
+ --theme-scrollbar-color-active: rgba(0, 0, 0, 0.36);
+ }
+ .dark-mode {
+ --theme-button-1: #f6f6f6;
+ --theme-button-1-color: #000;
+ --theme-button-1-hover: #e7e7e7;
+
+ --theme-color-green: #00b648;
+ --theme-color-red: #dc1b19;
+ --theme-color-yellow: #ffc90d;
+ --theme-color-blue: #4eb3ec;
+ --theme-color-orange: #ff8d4d;
+ --theme-color-purple: #b191f9;
+
+ --theme-scrollbar-color: var(--theme-color-accent);
+ --theme-scrollbar-color-active: var(--theme-color-accent);
+ }
+ body {margin: 0; padding: 0;}
+ .dark-mode .show-api-client-button span,
+ .light-mode .show-api-client-button span,
+ .light-mode .show-api-client-button svg,
+ .dark-mode .show-api-client-button svg {
+ color: white;
+ }
+ .t-doc__header .header-item-logo {
+ height: 32px;
+ }
+"#;
diff --git a/crates/oapi/src/swagger_ui/mod.rs b/crates/oapi/src/swagger_ui/mod.rs
index ad61f0c9d..4914029b4 100644
--- a/crates/oapi/src/swagger_ui/mod.rs
+++ b/crates/oapi/src/swagger_ui/mod.rs
@@ -78,11 +78,11 @@ const INDEX_TMPL: &str = r#"
pub struct SwaggerUi {
config: Config<'static>,
/// The title of the html page. The default title is "Swagger UI".
- pub title: String,
+ pub title: Cow<'static, str>,
/// The keywords of the html page.
- pub keywords: Option,
+ pub keywords: Option>,
/// The description of the html page.
- pub description: Option,
+ pub description: Option>,
}
impl SwaggerUi {
/// Create a new [`SwaggerUi`] for given path.
@@ -106,19 +106,19 @@ impl SwaggerUi {
}
/// Set title of the html page. The default title is "Swagger UI".
- pub fn title(mut self, title: impl Into) -> Self {
+ pub fn title(mut self, title: impl Into>) -> Self {
self.title = title.into();
self
}
/// Set keywords of the html page.
- pub fn keywords(mut self, keywords: impl Into) -> Self {
+ pub fn keywords(mut self, keywords: impl Into>) -> Self {
self.keywords = Some(keywords.into());
self
}
/// Set description of the html page.
- pub fn description(mut self, description: impl Into) -> Self {
+ pub fn description(mut self, description: impl Into>) -> Self {
self.description = Some(description.into());
self
}
diff --git a/examples/db-sea-orm/templates/layout.html.tera b/examples/db-sea-orm/templates/layout.html.tera
index abebef281..a6ca53403 100644
--- a/examples/db-sea-orm/templates/layout.html.tera
+++ b/examples/db-sea-orm/templates/layout.html.tera
@@ -1,11 +1,11 @@
-
+
Salvo Example
-
-
-
+
+
+
-
-
-
-
+
+
+
+
-
+ >
+
-
+