mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-04-24 17:48:34 +02:00
feat(bar): add logging and config hotwatch
This commit is contained in:
4
Cargo.lock
generated
4
Cargo.lock
generated
@@ -2686,6 +2686,7 @@ dependencies = [
|
|||||||
"eframe",
|
"eframe",
|
||||||
"egui-phosphor",
|
"egui-phosphor",
|
||||||
"font-loader",
|
"font-loader",
|
||||||
|
"hotwatch",
|
||||||
"image",
|
"image",
|
||||||
"komorebi-client",
|
"komorebi-client",
|
||||||
"netdev",
|
"netdev",
|
||||||
@@ -2695,6 +2696,9 @@ dependencies = [
|
|||||||
"serde_yaml 0.8.26",
|
"serde_yaml 0.8.26",
|
||||||
"starship-battery",
|
"starship-battery",
|
||||||
"sysinfo 0.31.3",
|
"sysinfo 0.31.3",
|
||||||
|
"tracing",
|
||||||
|
"tracing-appender",
|
||||||
|
"tracing-subscriber",
|
||||||
"windows 0.54.0",
|
"windows 0.54.0",
|
||||||
"windows-icons",
|
"windows-icons",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -10,12 +10,14 @@ komorebi-client = { path = "../komorebi-client" }
|
|||||||
|
|
||||||
catppuccin-egui = { version = "5.1", default-features = false, features = ["egui28"] }
|
catppuccin-egui = { version = "5.1", default-features = false, features = ["egui28"] }
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
|
clap = { version = "4", features = ["derive", "wrap_help"] }
|
||||||
color-eyre = "0.6"
|
color-eyre = "0.6"
|
||||||
crossbeam-channel = "0.5"
|
crossbeam-channel = "0.5"
|
||||||
dirs = { workspace = true }
|
dirs = { workspace = true }
|
||||||
eframe = "0.28"
|
eframe = "0.28"
|
||||||
egui-phosphor = "0.6.0"
|
egui-phosphor = "0.6.0"
|
||||||
font-loader = "0.11"
|
font-loader = "0.11"
|
||||||
|
hotwatch = "0.5"
|
||||||
image = "0.25"
|
image = "0.25"
|
||||||
netdev = "0.30"
|
netdev = "0.30"
|
||||||
schemars = { workspace = true }
|
schemars = { workspace = true }
|
||||||
@@ -24,6 +26,8 @@ serde_json = { workspace = true }
|
|||||||
serde_yaml = "0.8"
|
serde_yaml = "0.8"
|
||||||
starship-battery = "0.10"
|
starship-battery = "0.10"
|
||||||
sysinfo = "0.31"
|
sysinfo = "0.31"
|
||||||
|
tracing = "0.1"
|
||||||
|
tracing-appender = "0.2"
|
||||||
|
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||||
windows = { workspace = true }
|
windows = { workspace = true }
|
||||||
windows-icons = "0.1"
|
windows-icons = "0.1"
|
||||||
clap = { version = "4", features = ["derive", "wrap_help"] }
|
|
||||||
|
|||||||
@@ -14,47 +14,56 @@ use eframe::egui::FontFamily;
|
|||||||
use eframe::egui::Frame;
|
use eframe::egui::Frame;
|
||||||
use eframe::egui::Layout;
|
use eframe::egui::Layout;
|
||||||
use eframe::egui::Margin;
|
use eframe::egui::Margin;
|
||||||
|
use eframe::egui::Style;
|
||||||
use font_loader::system_fonts;
|
use font_loader::system_fonts;
|
||||||
use font_loader::system_fonts::FontPropertyBuilder;
|
use font_loader::system_fonts::FontPropertyBuilder;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::ops::Deref;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub struct Komobar {
|
pub struct Komobar {
|
||||||
pub config: KomobarConfig,
|
pub config: Arc<KomobarConfig>,
|
||||||
pub komorebi_notification_state: Option<Rc<RefCell<KomorebiNotificationState>>>,
|
pub komorebi_notification_state: Option<Rc<RefCell<KomorebiNotificationState>>>,
|
||||||
pub left_widgets: Vec<Box<dyn BarWidget>>,
|
pub left_widgets: Vec<Box<dyn BarWidget>>,
|
||||||
pub right_widgets: Vec<Box<dyn BarWidget>>,
|
pub right_widgets: Vec<Box<dyn BarWidget>>,
|
||||||
pub rx_gui: Receiver<komorebi_client::Notification>,
|
pub rx_gui: Receiver<komorebi_client::Notification>,
|
||||||
|
pub rx_config: Receiver<KomobarConfig>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Komobar {
|
impl Komobar {
|
||||||
pub fn new(
|
pub fn apply_config(
|
||||||
cc: &eframe::CreationContext<'_>,
|
&mut self,
|
||||||
rx_gui: Receiver<komorebi_client::Notification>,
|
ctx: &Context,
|
||||||
config: Arc<KomobarConfig>,
|
config: &KomobarConfig,
|
||||||
) -> Self {
|
previous_notification_state: Option<Rc<RefCell<KomorebiNotificationState>>>,
|
||||||
|
) {
|
||||||
if let Some(font_family) = &config.font_family {
|
if let Some(font_family) = &config.font_family {
|
||||||
Self::add_custom_font(&cc.egui_ctx, font_family);
|
tracing::info!("attempting to add custom font family: {font_family}");
|
||||||
|
Self::add_custom_font(ctx, font_family);
|
||||||
}
|
}
|
||||||
|
|
||||||
match config.theme {
|
match config.theme {
|
||||||
Some(Theme::CatppuccinFrappe) => {
|
Some(Theme::CatppuccinFrappe) => {
|
||||||
catppuccin_egui::set_theme(&cc.egui_ctx, catppuccin_egui::FRAPPE);
|
catppuccin_egui::set_theme(ctx, catppuccin_egui::FRAPPE);
|
||||||
|
tracing::info!("theme updated: Catppuccin Frappe");
|
||||||
}
|
}
|
||||||
Some(Theme::CatppuccinMacchiato) => {
|
Some(Theme::CatppuccinMacchiato) => {
|
||||||
catppuccin_egui::set_theme(&cc.egui_ctx, catppuccin_egui::MACCHIATO);
|
catppuccin_egui::set_theme(ctx, catppuccin_egui::MACCHIATO);
|
||||||
|
tracing::info!("theme updated: Catppuccin Macchiato");
|
||||||
}
|
}
|
||||||
Some(Theme::CatppuccinMocha) => {
|
Some(Theme::CatppuccinMocha) => {
|
||||||
catppuccin_egui::set_theme(&cc.egui_ctx, catppuccin_egui::MOCHA);
|
catppuccin_egui::set_theme(ctx, catppuccin_egui::MOCHA);
|
||||||
|
tracing::info!("theme updated: Catppuccin Mocha");
|
||||||
|
}
|
||||||
|
Some(Theme::Default) | None => {
|
||||||
|
ctx.set_style(Style::default());
|
||||||
|
tracing::info!("theme updated: Egui Default");
|
||||||
}
|
}
|
||||||
Some(Theme::Default) | None => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut komorebi_widget = None;
|
let mut komorebi_widget = None;
|
||||||
let mut komorebi_widget_idx = None;
|
let mut komorebi_widget_idx = None;
|
||||||
let mut komorebi_notification_state = None;
|
let mut komorebi_notification_state = previous_notification_state;
|
||||||
let mut side = None;
|
let mut side = None;
|
||||||
|
|
||||||
for (idx, widget_config) in config.left_widgets.iter().enumerate() {
|
for (idx, widget_config) in config.left_widgets.iter().enumerate() {
|
||||||
@@ -85,9 +94,22 @@ impl Komobar {
|
|||||||
.map(|config| config.as_boxed_bar_widget())
|
.map(|config| config.as_boxed_bar_widget())
|
||||||
.collect::<Vec<Box<dyn BarWidget>>>();
|
.collect::<Vec<Box<dyn BarWidget>>>();
|
||||||
|
|
||||||
if let (Some(idx), Some(widget), Some(side)) = (komorebi_widget_idx, komorebi_widget, side)
|
if let (Some(idx), Some(mut widget), Some(side)) =
|
||||||
|
(komorebi_widget_idx, komorebi_widget, side)
|
||||||
{
|
{
|
||||||
komorebi_notification_state = Some(widget.komorebi_notification_state.clone());
|
match komorebi_notification_state {
|
||||||
|
None => {
|
||||||
|
komorebi_notification_state = Some(widget.komorebi_notification_state.clone());
|
||||||
|
}
|
||||||
|
Some(ref previous) => {
|
||||||
|
previous
|
||||||
|
.borrow_mut()
|
||||||
|
.update_from_config(&widget.komorebi_notification_state.borrow());
|
||||||
|
|
||||||
|
widget.komorebi_notification_state = previous.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let boxed: Box<dyn BarWidget> = Box::new(widget);
|
let boxed: Box<dyn BarWidget> = Box::new(widget);
|
||||||
match side {
|
match side {
|
||||||
Side::Left => left_widgets[idx] = boxed,
|
Side::Left => left_widgets[idx] = boxed,
|
||||||
@@ -97,13 +119,31 @@ impl Komobar {
|
|||||||
|
|
||||||
right_widgets.reverse();
|
right_widgets.reverse();
|
||||||
|
|
||||||
Self {
|
self.left_widgets = left_widgets;
|
||||||
config: config.deref().clone(),
|
self.right_widgets = right_widgets;
|
||||||
komorebi_notification_state,
|
|
||||||
left_widgets,
|
tracing::info!("widget configuration options applied");
|
||||||
right_widgets,
|
|
||||||
|
self.komorebi_notification_state = komorebi_notification_state;
|
||||||
|
}
|
||||||
|
pub fn new(
|
||||||
|
cc: &eframe::CreationContext<'_>,
|
||||||
|
rx_gui: Receiver<komorebi_client::Notification>,
|
||||||
|
rx_config: Receiver<KomobarConfig>,
|
||||||
|
config: Arc<KomobarConfig>,
|
||||||
|
) -> Self {
|
||||||
|
let mut komobar = Self {
|
||||||
|
config: config.clone(),
|
||||||
|
komorebi_notification_state: None,
|
||||||
|
left_widgets: vec![],
|
||||||
|
right_widgets: vec![],
|
||||||
rx_gui,
|
rx_gui,
|
||||||
}
|
rx_config,
|
||||||
|
};
|
||||||
|
|
||||||
|
komobar.apply_config(&cc.egui_ctx, &config, None);
|
||||||
|
|
||||||
|
komobar
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_custom_font(ctx: &Context, name: &str) {
|
fn add_custom_font(ctx: &Context, name: &str) {
|
||||||
@@ -144,6 +184,14 @@ impl eframe::App for Komobar {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
fn update(&mut self, ctx: &Context, _frame: &mut eframe::Frame) {
|
fn update(&mut self, ctx: &Context, _frame: &mut eframe::Frame) {
|
||||||
|
if let Ok(updated_config) = self.rx_config.try_recv() {
|
||||||
|
self.apply_config(
|
||||||
|
ctx,
|
||||||
|
&updated_config,
|
||||||
|
self.komorebi_notification_state.clone(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(komorebi_notification_state) = &self.komorebi_notification_state {
|
if let Some(komorebi_notification_state) = &self.komorebi_notification_state {
|
||||||
komorebi_notification_state
|
komorebi_notification_state
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
|
|||||||
@@ -163,6 +163,10 @@ pub struct KomorebiNotificationState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl KomorebiNotificationState {
|
impl KomorebiNotificationState {
|
||||||
|
pub fn update_from_config(&mut self, config: &Self) {
|
||||||
|
self.hide_empty_workspaces = config.hide_empty_workspaces;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn handle_notification(
|
pub fn handle_notification(
|
||||||
&mut self,
|
&mut self,
|
||||||
monitor_index: usize,
|
monitor_index: usize,
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ use crate::config::KomobarConfig;
|
|||||||
use crate::config::Position;
|
use crate::config::Position;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use eframe::egui::ViewportBuilder;
|
use eframe::egui::ViewportBuilder;
|
||||||
|
use hotwatch::EventKind;
|
||||||
|
use hotwatch::Hotwatch;
|
||||||
use komorebi_client::SocketMessage;
|
use komorebi_client::SocketMessage;
|
||||||
use schemars::gen::SchemaSettings;
|
use schemars::gen::SchemaSettings;
|
||||||
use std::io::BufReader;
|
use std::io::BufReader;
|
||||||
@@ -22,6 +24,7 @@ use std::io::Read;
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use tracing_subscriber::EnvFilter;
|
||||||
|
|
||||||
pub static WIDGET_SPACING: f32 = 10.0;
|
pub static WIDGET_SPACING: f32 = 10.0;
|
||||||
|
|
||||||
@@ -36,7 +39,7 @@ struct Opts {
|
|||||||
config: Option<PathBuf>,
|
config: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> eframe::Result<()> {
|
fn main() -> color_eyre::Result<()> {
|
||||||
let opts: Opts = Opts::parse();
|
let opts: Opts = Opts::parse();
|
||||||
|
|
||||||
if opts.schema {
|
if opts.schema {
|
||||||
@@ -48,12 +51,28 @@ fn main() -> eframe::Result<()> {
|
|||||||
|
|
||||||
let gen = settings.into_generator();
|
let gen = settings.into_generator();
|
||||||
let socket_message = gen.into_root_schema_for::<KomobarConfig>();
|
let socket_message = gen.into_root_schema_for::<KomobarConfig>();
|
||||||
let schema = serde_json::to_string_pretty(&socket_message).unwrap();
|
let schema = serde_json::to_string_pretty(&socket_message)?;
|
||||||
|
|
||||||
println!("{schema}");
|
println!("{schema}");
|
||||||
std::process::exit(0);
|
std::process::exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if std::env::var("RUST_LIB_BACKTRACE").is_err() {
|
||||||
|
std::env::set_var("RUST_LIB_BACKTRACE", "1");
|
||||||
|
}
|
||||||
|
|
||||||
|
color_eyre::install()?;
|
||||||
|
|
||||||
|
if std::env::var("RUST_LOG").is_err() {
|
||||||
|
std::env::set_var("RUST_LOG", "info");
|
||||||
|
}
|
||||||
|
|
||||||
|
tracing::subscriber::set_global_default(
|
||||||
|
tracing_subscriber::fmt::Subscriber::builder()
|
||||||
|
.with_env_filter(EnvFilter::from_default_env())
|
||||||
|
.finish(),
|
||||||
|
)?;
|
||||||
|
|
||||||
let home_dir: PathBuf = std::env::var("KOMOREBI_CONFIG_HOME").map_or_else(
|
let home_dir: PathBuf = std::env::var("KOMOREBI_CONFIG_HOME").map_or_else(
|
||||||
|_| dirs::home_dir().expect("there is no home directory"),
|
|_| dirs::home_dir().expect("there is no home directory"),
|
||||||
|home_path| {
|
|home_path| {
|
||||||
@@ -89,10 +108,19 @@ fn main() -> eframe::Result<()> {
|
|||||||
"no komorebi.bar.json or komorebi.bar.yaml found in {}",
|
"no komorebi.bar.json or komorebi.bar.yaml found in {}",
|
||||||
home_dir.as_path().to_string_lossy()
|
home_dir.as_path().to_string_lossy()
|
||||||
),
|
),
|
||||||
Some(config) => KomobarConfig::read(&config).unwrap(),
|
Some(ref config) => {
|
||||||
|
tracing::info!(
|
||||||
|
"found configuration file: {}",
|
||||||
|
config.as_path().to_string_lossy()
|
||||||
|
);
|
||||||
|
|
||||||
|
KomobarConfig::read(config)?
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut builder = ViewportBuilder::default()
|
let config_path = config_path.unwrap();
|
||||||
|
|
||||||
|
let mut viewport_builder = ViewportBuilder::default()
|
||||||
.with_decorations(false)
|
.with_decorations(false)
|
||||||
// .with_transparent(config.transparent)
|
// .with_transparent(config.transparent)
|
||||||
.with_taskbar(false)
|
.with_taskbar(false)
|
||||||
@@ -100,8 +128,7 @@ fn main() -> eframe::Result<()> {
|
|||||||
.with_inner_size({
|
.with_inner_size({
|
||||||
let state = serde_json::from_str::<komorebi_client::State>(
|
let state = serde_json::from_str::<komorebi_client::State>(
|
||||||
&komorebi_client::send_query(&SocketMessage::State).unwrap(),
|
&komorebi_client::send_query(&SocketMessage::State).unwrap(),
|
||||||
)
|
)?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Position {
|
Position {
|
||||||
x: state.monitors.elements()[config.monitor.index].size().right as f32,
|
x: state.monitors.elements()[config.monitor.index].size().right as f32,
|
||||||
@@ -111,18 +138,18 @@ fn main() -> eframe::Result<()> {
|
|||||||
|
|
||||||
if let Some(viewport) = &config.viewport {
|
if let Some(viewport) = &config.viewport {
|
||||||
if let Some(position) = &viewport.position {
|
if let Some(position) = &viewport.position {
|
||||||
let b = builder.clone();
|
let b = viewport_builder.clone();
|
||||||
builder = b.with_position(*position);
|
viewport_builder = b.with_position(*position);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(inner_size) = &viewport.inner_size {
|
if let Some(inner_size) = &viewport.inner_size {
|
||||||
let b = builder.clone();
|
let b = viewport_builder.clone();
|
||||||
builder = b.with_inner_size(*inner_size);
|
viewport_builder = b.with_inner_size(*inner_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let native_options = eframe::NativeOptions {
|
let native_options = eframe::NativeOptions {
|
||||||
viewport: builder,
|
viewport: viewport_builder,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -130,13 +157,35 @@ fn main() -> eframe::Result<()> {
|
|||||||
komorebi_client::send_message(&SocketMessage::MonitorWorkAreaOffset(
|
komorebi_client::send_message(&SocketMessage::MonitorWorkAreaOffset(
|
||||||
config.monitor.index,
|
config.monitor.index,
|
||||||
*rect,
|
*rect,
|
||||||
))
|
))?;
|
||||||
.unwrap();
|
tracing::info!(
|
||||||
|
"work area offset applied to monitor: {}",
|
||||||
|
config.monitor.index
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let (tx_gui, rx_gui) = crossbeam_channel::unbounded();
|
let (tx_gui, rx_gui) = crossbeam_channel::unbounded();
|
||||||
let config_arc = Arc::new(config);
|
let (tx_config, rx_config) = crossbeam_channel::unbounded();
|
||||||
|
|
||||||
|
let mut hotwatch = Hotwatch::new()?;
|
||||||
|
let config_path_cl = config_path.clone();
|
||||||
|
|
||||||
|
hotwatch.watch(config_path, move |event| match event.kind {
|
||||||
|
EventKind::Modify(_) | EventKind::Remove(_) => {
|
||||||
|
let updated = KomobarConfig::read(&config_path_cl).unwrap();
|
||||||
|
tx_config.send(updated).unwrap();
|
||||||
|
|
||||||
|
tracing::info!(
|
||||||
|
"configuration file updated: {}",
|
||||||
|
config_path_cl.as_path().to_string_lossy()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
tracing::info!("watching configuration file for changes");
|
||||||
|
|
||||||
|
let config_arc = Arc::new(config);
|
||||||
eframe::run_native(
|
eframe::run_native(
|
||||||
"komorebi-bar",
|
"komorebi-bar",
|
||||||
native_options,
|
native_options,
|
||||||
@@ -152,6 +201,7 @@ fn main() -> eframe::Result<()> {
|
|||||||
let ctx_komorebi = cc.egui_ctx.clone();
|
let ctx_komorebi = cc.egui_ctx.clone();
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
let listener = komorebi_client::subscribe("komorebi-bar").unwrap();
|
let listener = komorebi_client::subscribe("komorebi-bar").unwrap();
|
||||||
|
tracing::info!("subscribed to komorebi notifications: \"komorebi-bar\"");
|
||||||
|
|
||||||
for client in listener.incoming() {
|
for client in listener.incoming() {
|
||||||
match client {
|
match client {
|
||||||
@@ -161,6 +211,8 @@ fn main() -> eframe::Result<()> {
|
|||||||
|
|
||||||
// this is when we know a shutdown has been sent
|
// this is when we know a shutdown has been sent
|
||||||
if matches!(reader.read_to_end(&mut buffer), Ok(0)) {
|
if matches!(reader.read_to_end(&mut buffer), Ok(0)) {
|
||||||
|
tracing::info!("disconnected from komorebi");
|
||||||
|
|
||||||
// keep trying to reconnect to komorebi
|
// keep trying to reconnect to komorebi
|
||||||
while komorebi_client::send_message(
|
while komorebi_client::send_message(
|
||||||
&SocketMessage::AddSubscriberSocket(String::from(
|
&SocketMessage::AddSubscriberSocket(String::from(
|
||||||
@@ -172,7 +224,8 @@ fn main() -> eframe::Result<()> {
|
|||||||
std::thread::sleep(Duration::from_secs(1));
|
std::thread::sleep(Duration::from_secs(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
// here we have reconnected
|
tracing::info!("reconnected to komorebi");
|
||||||
|
|
||||||
if let Some(rect) = &config_cl.monitor.work_area_offset {
|
if let Some(rect) = &config_cl.monitor.work_area_offset {
|
||||||
while komorebi_client::send_message(
|
while komorebi_client::send_message(
|
||||||
&SocketMessage::MonitorWorkAreaOffset(
|
&SocketMessage::MonitorWorkAreaOffset(
|
||||||
@@ -192,18 +245,20 @@ fn main() -> eframe::Result<()> {
|
|||||||
&String::from_utf8(buffer).unwrap(),
|
&String::from_utf8(buffer).unwrap(),
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
tracing::debug!("received notification from komorebi");
|
||||||
tx_gui.send(notification).unwrap();
|
tx_gui.send(notification).unwrap();
|
||||||
ctx_komorebi.request_repaint();
|
ctx_komorebi.request_repaint();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
dbg!(error);
|
tracing::error!("{error}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(Box::new(Komobar::new(cc, rx_gui, config_arc)))
|
Ok(Box::new(Komobar::new(cc, rx_gui, rx_config, config_arc)))
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
.map_err(|error| color_eyre::eyre::Error::msg(error.to_string()))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user