feat(settings): add grouped configuration hub, languages and tray controls
This commit is contained in:
1 parent
6f34fd080f
commit
cc3b6f903f
90 files changed
+3625
-408
No files matched your search
@@ -9,6 +9,7 @@ rust-version.workspace = true
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
schemars.workspace = true
|
||||
ferese-activities = { path = "../crates/ferese-activities" }
|
||||
ferese-panel = { path = "../crates/ferese-panel" }
|
||||
ferese-shape.workspace = true
|
||||
|
||||
+31
-20
@@ -36,8 +36,10 @@ pub(crate) use tab_indicator::TabIndicatorSettings;
|
||||
use crate::window_rules;
|
||||
use crate::window_rules::{WindowRule, WindowRuleConfig};
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
#[derive(Debug, Default, Deserialize, schemars::JsonSchema, serde::Serialize)]
|
||||
pub struct Config {
|
||||
#[serde(default)]
|
||||
ui: ferese_config::locale::Settings,
|
||||
#[serde(default)]
|
||||
activities: ferese_config::activities::Settings,
|
||||
#[serde(default)]
|
||||
@@ -79,10 +81,10 @@ pub struct Config {
|
||||
#[serde(default)]
|
||||
pub(crate) xwayland: XwaylandConfig,
|
||||
#[serde(default, rename = "status")]
|
||||
_status: ShellStatusConfig,
|
||||
_status: ferese_config::status::Settings,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, schemars::JsonSchema, serde::Serialize)]
|
||||
pub(crate) struct DaemonConfig {
|
||||
pub command: Vec<String>,
|
||||
#[serde(default = "default_true")]
|
||||
@@ -93,16 +95,6 @@ pub(crate) struct DaemonConfig {
|
||||
pub nested: bool,
|
||||
}
|
||||
|
||||
// Validate shell-only fields too, before publishing an accepted source.
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
#[allow(dead_code)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
struct ShellStatusConfig {
|
||||
keybinding_guide: Option<bool>,
|
||||
low_battery_threshold: Option<u8>,
|
||||
settings_command: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
#[cfg(test)]
|
||||
pub(crate) fn parse_source(source: &str) -> Result<Self, ConfigError> {
|
||||
@@ -129,6 +121,7 @@ impl Config {
|
||||
}
|
||||
|
||||
pub(crate) fn runtime_config(&self) -> Result<crate::RuntimeConfig, ConfigError> {
|
||||
self._status.validate().map_err(ConfigError::Binding)?;
|
||||
if let Some(panels) = &self.panels {
|
||||
ferese_panel::validate(panels).map_err(ConfigError::Binding)?;
|
||||
}
|
||||
@@ -212,13 +205,13 @@ impl Config {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
#[derive(Debug, Default, Deserialize, schemars::JsonSchema, serde::Serialize)]
|
||||
struct WorkspacesConfig {
|
||||
#[serde(default)]
|
||||
auto_back_and_forth: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Debug, Deserialize, schemars::JsonSchema, serde::Serialize)]
|
||||
struct LayoutConfig {
|
||||
mode: Option<LayoutModeValue>,
|
||||
#[serde(default = "default_inner_gap")]
|
||||
@@ -240,14 +233,14 @@ impl Default for LayoutConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, schemars::JsonSchema, serde::Serialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
enum LayoutModeValue {
|
||||
Scrolling,
|
||||
Tree,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
#[derive(Debug, Default, Deserialize, schemars::JsonSchema, serde::Serialize)]
|
||||
struct ScrollingConfig {
|
||||
#[serde(default)]
|
||||
tab_indicator: tab_indicator::TabIndicatorConfig,
|
||||
@@ -258,14 +251,14 @@ struct ScrollingConfig {
|
||||
width_presets: Vec<ColumnWidthValue>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, schemars::JsonSchema, serde::Serialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
enum ColumnDisplayValue {
|
||||
Stacked,
|
||||
Tabbed,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, schemars::JsonSchema, serde::Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
enum FocusStrategyValue {
|
||||
Minimal,
|
||||
@@ -273,7 +266,7 @@ enum FocusStrategyValue {
|
||||
Paged,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[derive(Clone, Debug, Deserialize, schemars::JsonSchema, serde::Serialize)]
|
||||
#[serde(untagged)]
|
||||
enum ColumnWidthValue {
|
||||
Proportion(f64),
|
||||
@@ -428,6 +421,24 @@ pub(crate) fn config_path() -> Option<PathBuf> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn configuration_schema_exposes_effective_panel_defaults_and_typed_values() {
|
||||
let exported = crate::configuration_schema();
|
||||
assert_eq!(exported["version"], 1);
|
||||
assert_eq!(
|
||||
exported["defaults"]["panels"],
|
||||
serde_json::to_value(
|
||||
Config::default()
|
||||
.panels
|
||||
.unwrap_or_else(|| vec![ferese_panel::Panel::default()])
|
||||
)
|
||||
.unwrap()
|
||||
);
|
||||
let config: Config = serde_json::from_value(exported["defaults"].clone()).unwrap();
|
||||
config.runtime_config().unwrap();
|
||||
assert!(exported["schema"]["properties"].get("ui").is_some());
|
||||
assert!(exported["schema"]["properties"].get("status").is_some());
|
||||
}
|
||||
#[test]
|
||||
fn geometry_rejects_removed_and_misspelled_keys_in_both_consumption_paths() {
|
||||
for key in [
|
||||
|
||||
@@ -202,14 +202,14 @@ pub struct BorderGradient {
|
||||
pub angle: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
#[derive(Debug, Default, Deserialize, schemars::JsonSchema, serde::Serialize)]
|
||||
pub(super) struct BorderPaintConfig {
|
||||
#[serde(default)]
|
||||
pub(super) style: ferese_config::theme::PaintStyle,
|
||||
pub(super) gradient: Option<BorderGradientConfig>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Debug, Deserialize, schemars::JsonSchema, serde::Serialize)]
|
||||
pub(super) struct BorderGradientConfig {
|
||||
pub(super) from: String,
|
||||
pub(super) to: String,
|
||||
@@ -278,7 +278,9 @@ pub struct ThemeSettings {
|
||||
pub reduce_transparency: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
|
||||
#[derive(
|
||||
Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, schemars::JsonSchema, serde::Serialize,
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum MaterialStyle {
|
||||
Translucent,
|
||||
@@ -286,7 +288,7 @@ pub enum MaterialStyle {
|
||||
Solid,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
#[derive(Debug, Default, Deserialize, schemars::JsonSchema, serde::Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub(super) struct AppearanceConfig {
|
||||
#[serde(default)]
|
||||
@@ -303,7 +305,7 @@ pub struct FocusEffectSettings {
|
||||
pub duration_ms: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Debug, Deserialize, schemars::JsonSchema, serde::Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub(super) struct FocusEffectConfig {
|
||||
pub(super) enabled: bool,
|
||||
@@ -327,7 +329,7 @@ impl Default for FocusEffectConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
#[derive(Debug, Default, Deserialize, schemars::JsonSchema, serde::Serialize)]
|
||||
pub(super) struct ThemeConfig {
|
||||
#[serde(default)]
|
||||
pub(super) border: BorderPaintConfig,
|
||||
@@ -349,23 +351,23 @@ pub(super) struct ThemeConfig {
|
||||
pub(super) surface: ThemeSurfaceConfig,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
#[derive(Debug, Default, Deserialize, schemars::JsonSchema, serde::Serialize)]
|
||||
pub(super) struct ThemeSurfaceConfig {
|
||||
#[serde(default)]
|
||||
pub(super) bar: ThemeBarConfig,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
#[derive(Debug, Default, Deserialize, schemars::JsonSchema, serde::Serialize)]
|
||||
pub(super) struct ThemeBarConfig {
|
||||
pub(super) background: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
#[derive(Debug, Default, Deserialize, schemars::JsonSchema, serde::Serialize)]
|
||||
pub(super) struct OverviewTypographyConfig {
|
||||
pub(super) font_family: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Debug, Deserialize, schemars::JsonSchema, serde::Serialize)]
|
||||
pub(super) struct ThemeMaterialConfig {
|
||||
#[serde(default = "default_shell_opacity")]
|
||||
pub(super) opacity: f64,
|
||||
@@ -400,7 +402,7 @@ fn default_backdrop_blur() -> f64 {
|
||||
12.0
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Debug, Deserialize, schemars::JsonSchema, serde::Serialize)]
|
||||
pub(super) struct ThemeColorsConfig {
|
||||
#[serde(default = "default_text_primary_color")]
|
||||
pub(super) text_primary: String,
|
||||
@@ -429,13 +431,13 @@ impl Default for ThemeColorsConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
#[derive(Debug, Default, Deserialize, schemars::JsonSchema, serde::Serialize)]
|
||||
pub(super) struct ThemeShadowConfig {
|
||||
#[serde(default)]
|
||||
pub(super) soft: SoftShadowConfig,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Debug, Deserialize, schemars::JsonSchema, serde::Serialize)]
|
||||
pub(super) struct SoftShadowConfig {
|
||||
#[serde(default = "default_shadow_offset_y")]
|
||||
pub(super) offset_y: f64,
|
||||
@@ -455,7 +457,7 @@ impl Default for SoftShadowConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Debug, Deserialize, schemars::JsonSchema, serde::Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub(super) struct ThemeGeometryConfig {
|
||||
#[serde(default, rename = "control_gap")]
|
||||
|
||||
@@ -832,7 +832,18 @@ impl Binding {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, PartialEq)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
Default,
|
||||
Deserialize,
|
||||
Eq,
|
||||
Hash,
|
||||
PartialEq,
|
||||
schemars::JsonSchema,
|
||||
serde::Serialize,
|
||||
)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub(super) enum BindingMatch {
|
||||
#[default]
|
||||
@@ -843,7 +854,7 @@ pub(super) enum BindingMatch {
|
||||
Pointer,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[derive(Clone, Debug, Deserialize, schemars::JsonSchema, serde::Serialize)]
|
||||
pub(super) struct BindingConfig {
|
||||
pub(super) keys: String,
|
||||
#[serde(default, rename = "match")]
|
||||
|
||||
@@ -6,7 +6,7 @@ use super::{
|
||||
#[cfg(test)]
|
||||
use ferese_layout::Direction;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Debug, Deserialize, schemars::JsonSchema, serde::Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub(crate) struct BindingModeConfig {
|
||||
name: String,
|
||||
|
||||
@@ -141,7 +141,7 @@ fn default_swipe_threshold() -> u16 {
|
||||
80
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Debug, Deserialize, schemars::JsonSchema, serde::Serialize)]
|
||||
pub(super) struct InputConfig {
|
||||
#[serde(default)]
|
||||
pub(super) focus_follows_mouse: bool,
|
||||
@@ -176,7 +176,7 @@ impl Default for InputConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Debug, Deserialize, schemars::JsonSchema, serde::Serialize)]
|
||||
pub(super) struct TouchpadConfig {
|
||||
#[serde(default)]
|
||||
pub(super) accel_speed: f64,
|
||||
@@ -257,14 +257,16 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq)]
|
||||
#[derive(
|
||||
Clone, Copy, Debug, Deserialize, PartialEq, Eq, schemars::JsonSchema, serde::Serialize,
|
||||
)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum AccelerationProfile {
|
||||
Adaptive,
|
||||
Flat,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, schemars::JsonSchema, serde::Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct MouseSettings {
|
||||
pub natural_scroll: bool,
|
||||
|
||||
@@ -191,7 +191,9 @@ pub struct OutputModeRequest {
|
||||
pub refresh_millihertz: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
|
||||
#[derive(
|
||||
Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, schemars::JsonSchema, serde::Serialize,
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum OutputTransform {
|
||||
#[default]
|
||||
@@ -211,7 +213,9 @@ pub enum OutputTransform {
|
||||
Flipped270,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize, serde::Serialize, PartialEq, Eq)]
|
||||
#[derive(
|
||||
Clone, Copy, Debug, Default, Deserialize, serde::Serialize, PartialEq, Eq, schemars::JsonSchema,
|
||||
)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum LidPolicy {
|
||||
#[default]
|
||||
@@ -223,7 +227,9 @@ const fn default_confirm_timeout() -> u32 {
|
||||
15
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize, serde::Serialize, PartialEq, Eq)]
|
||||
#[derive(
|
||||
Clone, Copy, Debug, Default, Deserialize, serde::Serialize, PartialEq, Eq, schemars::JsonSchema,
|
||||
)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum OutputLayout {
|
||||
InternalOnly,
|
||||
@@ -233,7 +239,7 @@ pub enum OutputLayout {
|
||||
Mirror,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[derive(Clone, Debug, Deserialize, schemars::JsonSchema, serde::Serialize)]
|
||||
pub(super) struct OutputProfileConfig {
|
||||
pub(super) name: String,
|
||||
#[serde(default)]
|
||||
@@ -248,7 +254,7 @@ pub(super) struct OutputProfileConfig {
|
||||
pub(super) outputs: Vec<OutputConfig>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[derive(Clone, Debug, Deserialize, schemars::JsonSchema, serde::Serialize)]
|
||||
pub(super) struct OutputConfig {
|
||||
#[serde(default)]
|
||||
pub(super) auto_refresh: bool,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use super::{Config, ConfigError, Deserialize, parse_color};
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[derive(Clone, Debug, Deserialize, schemars::JsonSchema, serde::Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub(super) struct TabIndicatorConfig {
|
||||
width: f64,
|
||||
|
||||
@@ -2,7 +2,9 @@ use std::path::PathBuf;
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Eq)]
|
||||
#[derive(
|
||||
Clone, Copy, Debug, Default, Deserialize, PartialEq, Eq, schemars::JsonSchema, serde::Serialize,
|
||||
)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub(crate) enum XwaylandStartup {
|
||||
#[default]
|
||||
@@ -10,7 +12,7 @@ pub(crate) enum XwaylandStartup {
|
||||
Eager,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, schemars::JsonSchema, serde::Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub(crate) struct XwaylandConfig {
|
||||
pub enabled: bool,
|
||||
@@ -79,7 +81,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn startup_mode_accepts_kebab_case() {
|
||||
#[derive(Deserialize)]
|
||||
#[derive(Deserialize, schemars::JsonSchema, serde::Serialize)]
|
||||
struct Wrapper {
|
||||
startup: XwaylandStartup,
|
||||
}
|
||||
|
||||
@@ -11,7 +11,9 @@ use smithay::wayland::shell::xdg::XdgToplevelSurfaceData;
|
||||
|
||||
use crate::Ferese;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq)]
|
||||
#[derive(
|
||||
Clone, Copy, Debug, Deserialize, PartialEq, Eq, schemars::JsonSchema, serde::Serialize,
|
||||
)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub(crate) struct Settings {
|
||||
pub fullscreen_playback: bool,
|
||||
@@ -25,7 +27,9 @@ impl Default for Settings {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq)]
|
||||
#[derive(
|
||||
Clone, Copy, Debug, Deserialize, PartialEq, Eq, schemars::JsonSchema, serde::Serialize,
|
||||
)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum Mode {
|
||||
None,
|
||||
|
||||
@@ -65,12 +65,28 @@ use tracing::{info, warn};
|
||||
|
||||
use crate::backends::LaunchConfig;
|
||||
|
||||
fn configuration_schema() -> serde_json::Value {
|
||||
let mut schema =
|
||||
serde_json::to_value(schemars::schema_for!(config::Config)).expect("serializable schema");
|
||||
let mut defaults =
|
||||
serde_json::to_value(config::Config::default()).expect("serializable defaults");
|
||||
// None means the implicit default panel, not an empty desktop. Present its
|
||||
// effective value so the complete editor can modify it directly.
|
||||
defaults["panels"] = serde_json::json!([ferese_panel::Panel::default()]);
|
||||
schema["properties"]["panels"]["default"] = defaults["panels"].clone();
|
||||
serde_json::json!({"version":1,"schema":schema,"defaults":defaults})
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
init_logging();
|
||||
|
||||
// Settings validates a candidate before atomically replacing the user's file.
|
||||
// This mode never opens a display, input device, or compositor socket.
|
||||
let args = std::env::args_os().skip(1).collect::<Vec<_>>();
|
||||
if args == ["--config-schema"] || args == ["--config-schema", "--backend=schema-query"] {
|
||||
println!("{}", configuration_schema());
|
||||
return Ok(());
|
||||
}
|
||||
if args.first().is_some_and(|arg| arg == "--check-config") {
|
||||
if args.len() != 2 {
|
||||
return Err("usage: ferese --check-config PATH".into());
|
||||
|
||||
@@ -17,7 +17,7 @@ use smithay::wayland::session_lock::{
|
||||
|
||||
use crate::Ferese;
|
||||
|
||||
#[derive(Clone, Copy, Debug, serde::Deserialize)]
|
||||
#[derive(Clone, Copy, Debug, serde::Deserialize, schemars::JsonSchema, serde::Serialize)]
|
||||
#[serde(default)]
|
||||
pub(crate) struct IdleSettings {
|
||||
pub dim_after_seconds: u64,
|
||||
|
||||
@@ -27,7 +27,7 @@ use std::sync::Arc;
|
||||
|
||||
const UPLOAD_RETRY_DELAY: Duration = Duration::from_secs(5);
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, schemars::JsonSchema, serde::Serialize)]
|
||||
pub(crate) struct WallpaperConfig {
|
||||
#[serde(default = "default_wallpaper_path")]
|
||||
pub path: Option<PathBuf>,
|
||||
@@ -48,7 +48,9 @@ impl Default for WallpaperConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq)]
|
||||
#[derive(
|
||||
Clone, Copy, Debug, Default, Deserialize, PartialEq, schemars::JsonSchema, serde::Serialize,
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub(crate) enum WallpaperMode {
|
||||
#[default]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[derive(Clone, Debug, Deserialize, schemars::JsonSchema, serde::Serialize)]
|
||||
pub(crate) struct WindowRuleConfig {
|
||||
pub app_id: Option<String>,
|
||||
pub title: Option<String>,
|
||||
|
||||
Reference in new issue
Block a user