dioxus_docs_kit/config.rs
1//! Builder for constructing a `DocsRegistry`.
2
3use crate::registry::DocsRegistry;
4#[cfg(feature = "highlight")]
5use dioxus_code::Theme;
6use std::collections::HashMap;
7
8/// Theme configuration for the documentation site.
9///
10/// Controls which DaisyUI theme(s) are applied and whether a toggle button is shown.
11#[derive(Clone, Debug, PartialEq)]
12pub struct ThemeConfig {
13 /// The default theme name (must match a DaisyUI theme defined in `tailwind.css`).
14 pub default_theme: String,
15 /// If set, enables a light/dark toggle button. Tuple is `(light_theme, dark_theme)`.
16 pub toggle_themes: Option<(String, String)>,
17 /// localStorage key used to persist the user's theme preference.
18 pub storage_key: String,
19}
20
21/// How rendered code blocks pick their syntax-highlighting theme.
22///
23/// Defaults to [`CodeThemeConfig::Adaptive`] with GitHub Light / Tokyo Night.
24///
25/// Only available with the `highlight` feature (default), which pulls in `dioxus-code`.
26#[cfg(feature = "highlight")]
27#[derive(Clone, Copy, Debug)]
28#[non_exhaustive]
29pub enum CodeThemeConfig {
30 /// Always use this one theme, regardless of the site's light/dark state.
31 Fixed(Theme),
32 /// Pick `light` or `dark` to match the site theme.
33 ///
34 /// When a light/dark toggle is configured (via [`DocsConfig::with_theme_toggle`]),
35 /// the choice tracks the active `data-theme` (not the OS `prefers-color-scheme`),
36 /// so code blocks stay in sync with the toggle. Without a toggle it falls back to
37 /// `prefers-color-scheme`.
38 Adaptive {
39 /// Theme used when the site is in its light state.
40 light: Theme,
41 /// Theme used when the site is in its dark state.
42 dark: Theme,
43 },
44}
45
46#[cfg(feature = "highlight")]
47impl Default for CodeThemeConfig {
48 fn default() -> Self {
49 Self::Adaptive {
50 light: Theme::GITHUB_LIGHT,
51 dark: Theme::TOKYO_NIGHT,
52 }
53 }
54}
55
56/// Builder for constructing a [`DocsRegistry`].
57///
58/// # Example
59///
60/// ```rust,ignore
61/// let registry = DocsConfig::new(nav_json, content_map)
62/// // `.with_openapi()` needs the `openapi` feature (on by default)
63/// .with_openapi("api-reference", spec_yaml)
64/// .with_default_path("getting-started/introduction")
65/// .build();
66/// ```
67pub struct DocsConfig {
68 nav_json: String,
69 content_map: HashMap<&'static str, &'static str>,
70 #[cfg(feature = "openapi")]
71 openapi_specs: Vec<(String, String)>,
72 default_path: Option<String>,
73 api_group_name: Option<String>,
74 theme: Option<ThemeConfig>,
75 #[cfg(feature = "highlight")]
76 code_theme: CodeThemeConfig,
77}
78
79impl DocsConfig {
80 /// Create a new builder from a `_nav.json` string and a content map.
81 ///
82 /// The content map is typically generated by `build.rs` using `include_str!()`.
83 pub fn new(nav_json: &str, content_map: HashMap<&'static str, &'static str>) -> Self {
84 Self {
85 nav_json: nav_json.to_string(),
86 content_map,
87 #[cfg(feature = "openapi")]
88 openapi_specs: Vec::new(),
89 default_path: None,
90 api_group_name: None,
91 theme: None,
92 #[cfg(feature = "highlight")]
93 code_theme: CodeThemeConfig::default(),
94 }
95 }
96
97 /// Add an OpenAPI specification.
98 ///
99 /// - `prefix`: The URL prefix for this spec's endpoints (e.g. "api-reference").
100 /// - `yaml`: The raw YAML string of the OpenAPI spec.
101 ///
102 /// The `prefix` must correspond to a nav group in `_nav.json` whose `"group"` value
103 /// matches [`Self::with_api_group_name`] (defaults to `"API Reference"`). The library
104 /// dynamically injects API endpoints into that group's sidebar — do **not** list
105 /// individual operation paths in the `"pages"` array of `_nav.json`.
106 ///
107 /// Only available with the `openapi` feature (default), which pulls in
108 /// `openapiv3` and `serde_yaml`.
109 #[cfg(feature = "openapi")]
110 pub fn with_openapi(mut self, prefix: &str, yaml: &str) -> Self {
111 self.openapi_specs
112 .push((prefix.to_string(), yaml.to_string()));
113 self
114 }
115
116 /// Set the default documentation path for redirects.
117 ///
118 /// Defaults to the first page in the first nav group if not set.
119 pub fn with_default_path(mut self, path: &str) -> Self {
120 self.default_path = Some(path.to_string());
121 self
122 }
123
124 /// Set the display name for the API Reference sidebar group.
125 ///
126 /// Defaults to `"API Reference"`. The value must match a `"group"` in `_nav.json`
127 /// so the library knows where to inject the API endpoint sidebar entries.
128 /// See [`Self::with_openapi`] for details.
129 pub fn with_api_group_name(mut self, name: &str) -> Self {
130 self.api_group_name = Some(name.to_string());
131 self
132 }
133
134 /// Set a single theme (no toggle button).
135 ///
136 /// The theme name must match a DaisyUI theme defined in the consumer's `tailwind.css`.
137 pub fn with_theme(mut self, theme: &str) -> Self {
138 self.theme = Some(ThemeConfig {
139 default_theme: theme.to_string(),
140 toggle_themes: None,
141 storage_key: "docs-theme".to_string(),
142 });
143 self
144 }
145
146 /// Enable a light/dark theme toggle.
147 ///
148 /// - `light`: Name of the light DaisyUI theme.
149 /// - `dark`: Name of the dark DaisyUI theme.
150 /// - `default`: Which of the two to use on first visit (`light` or `dark`).
151 pub fn with_theme_toggle(mut self, light: &str, dark: &str, default: &str) -> Self {
152 self.theme = Some(ThemeConfig {
153 default_theme: default.to_string(),
154 toggle_themes: Some((light.to_string(), dark.to_string())),
155 storage_key: "docs-theme".to_string(),
156 });
157 self
158 }
159
160 /// Use a single, fixed syntax-highlighting theme for all code blocks.
161 ///
162 /// Use this for single-theme sites (e.g. a dark-only app) so the code block
163 /// background matches the site instead of following the reader's OS setting.
164 ///
165 /// Only available with the `highlight` feature (default), which pulls in `dioxus-code`.
166 #[cfg(feature = "highlight")]
167 pub fn with_code_theme(mut self, theme: Theme) -> Self {
168 self.code_theme = CodeThemeConfig::Fixed(theme);
169 self
170 }
171
172 /// Use a light/dark pair of syntax themes for code blocks.
173 ///
174 /// When a theme toggle is configured (see [`Self::with_theme_toggle`]), the active
175 /// choice tracks the toggle's `data-theme`; otherwise it follows the reader's OS
176 /// `prefers-color-scheme`. Defaults to GitHub Light / Tokyo Night when not set.
177 ///
178 /// Only available with the `highlight` feature (default), which pulls in `dioxus-code`.
179 #[cfg(feature = "highlight")]
180 pub fn with_code_themes(mut self, light: Theme, dark: Theme) -> Self {
181 self.code_theme = CodeThemeConfig::Adaptive { light, dark };
182 self
183 }
184
185 /// Build the [`DocsRegistry`].
186 ///
187 /// Parses all documents, builds the search index, and parses OpenAPI specs.
188 ///
189 /// # Panics
190 ///
191 /// Panics with a descriptive message if `_nav.json` or an OpenAPI spec fails
192 /// to parse. Use [`Self::try_build`] to handle these errors yourself.
193 pub fn build(self) -> DocsRegistry {
194 self.try_build()
195 .unwrap_or_else(|e| panic!("dioxus-docs-kit: {e}"))
196 }
197
198 /// Build the [`DocsRegistry`], returning an error instead of panicking when
199 /// `_nav.json` or an OpenAPI spec fails to parse.
200 pub fn try_build(self) -> Result<DocsRegistry, crate::error::DocsKitError> {
201 DocsRegistry::try_from_config(self)
202 }
203
204 // Accessors for DocsRegistry::from_config
205 pub(crate) fn nav_json(&self) -> &str {
206 &self.nav_json
207 }
208
209 pub(crate) fn content_map(&self) -> &HashMap<&'static str, &'static str> {
210 &self.content_map
211 }
212
213 #[cfg(feature = "openapi")]
214 pub(crate) fn openapi_specs(&self) -> &[(String, String)] {
215 &self.openapi_specs
216 }
217
218 pub(crate) fn default_path_value(&self) -> Option<&str> {
219 self.default_path.as_deref()
220 }
221
222 pub(crate) fn api_group_name_value(&self) -> Option<&str> {
223 self.api_group_name.as_deref()
224 }
225
226 pub(crate) fn theme_config(&self) -> Option<&ThemeConfig> {
227 self.theme.as_ref()
228 }
229
230 #[cfg(feature = "highlight")]
231 pub(crate) fn code_theme_value(&self) -> CodeThemeConfig {
232 self.code_theme
233 }
234}