use std::path::Path;
use clap::Parser;
use dais_ui::display_mode::{DisplayHints, DisplayMode};
#[derive(Parser, Debug)]
#[command(name = "dais", version, about)]
struct Cli {
pdf_path: Option<String>,
#[arg(long)]
config: Option<String>,
#[arg(long)]
single: bool,
#[arg(long, alias = "screen-share")]
screen_share: bool,
#[arg(long)]
edit: bool,
#[arg(long)]
test_input: bool,
}
fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt()
.with_env_filter(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info")),
)
.init();
let cli = Cli::parse();
if cli.test_input {
return run_test_input(&cli);
}
if cli.pdf_path.is_none() {
anyhow::bail!("Usage: dais <file.pdf>");
}
let pdf_path = cli.pdf_path.unwrap();
tracing::info!("Opening: {pdf_path}");
let explicit_config = cli.config.as_deref().map(Path::new);
let config = dais_core::config::load_config_for(Path::new(&pdf_path), explicit_config);
tracing::debug!("Config loaded: {config:?}");
let doc = dais_document::pdf_hayro::HayroDocument::open(Path::new(&pdf_path))?;
let page_count = {
use dais_document::source::DocumentSource;
doc.page_count()
};
tracing::info!("Document has {page_count} pages");
if cli.edit {
return run_grouping_editor(doc, &pdf_path, config.normalized_sidecar_format());
}
let embedded_pdfpc = {
use dais_document::source::DocumentSource;
doc.embedded_metadata().and_then(|m| m.pdfpc_data)
};
let (metadata, meta_source) =
dais_sidecar::metadata::load_metadata(Path::new(&pdf_path), embedded_pdfpc.as_deref());
tracing::info!("Metadata source: {meta_source:?}");
let monitor_mgr = dais_platform::create_monitor_manager();
let hints = DisplayHints { force_single: cli.single, force_screen_share: cli.screen_share };
let display_result =
dais_ui::display_mode::determine_display_mode(hints, &config, &monitor_mgr);
let display_warnings = display_result.warnings;
let audience_reassignment = display_result.audience_reassignment;
let display_mode = display_result.mode;
tracing::info!("Display mode: {display_mode:?}");
let is_screen_share = matches!(display_mode, DisplayMode::ScreenShare);
let bus = dais_core::bus::CommandBus::new();
let sender = bus.sender();
let receiver = bus.into_receiver();
let (engine, shared_state) = dais_engine::engine::PresentationEngine::new(
page_count,
&metadata,
&config,
receiver,
Path::new(&pdf_path).to_path_buf(),
);
if is_screen_share {
let _ = sender.send(dais_core::commands::Command::ToggleScreenShareMode);
}
if matches!(display_mode, DisplayMode::Single) {
let _ = sender.send(dais_core::commands::Command::TogglePresentationMode);
}
tracing::info!("Dais v{} starting", env!("CARGO_PKG_VERSION"));
let doc_arc: std::sync::Arc<dyn dais_document::source::DocumentSource> =
std::sync::Arc::new(doc);
let presenter_window_size = egui::vec2(1400.0, 850.0);
let native_options = eframe::NativeOptions {
viewport: dais_ui::display_mode::presenter_viewport_builder(
&config,
&monitor_mgr,
presenter_window_size,
),
..Default::default()
};
let config_clone = config.clone();
eframe::run_native(
"Dais",
native_options,
Box::new(move |_cc| {
let mut app = dais_ui::app::DaisApp::new(
engine,
shared_state,
doc_arc,
sender,
&config_clone,
display_mode,
);
app.set_audience_reassignment(audience_reassignment.clone());
for warning in &display_warnings {
app.toast_manager_mut()
.push(dais_ui::widgets::toast::ToastLevel::Warning, warning.clone());
}
Ok(Box::new(app))
}),
)
.map_err(|e| anyhow::anyhow!("eframe error: {e}"))?;
Ok(())
}
fn run_grouping_editor(
doc: dais_document::pdf_hayro::HayroDocument,
pdf_path: &str,
sidecar_format: &str,
) -> anyhow::Result<()> {
use dais_document::source::DocumentSource;
tracing::info!("Opening grouping editor");
let embedded_pdfpc = doc.embedded_metadata().and_then(|m| m.pdfpc_data);
let (metadata, meta_source) =
dais_sidecar::metadata::load_metadata(Path::new(pdf_path), embedded_pdfpc.as_deref());
tracing::info!("Metadata source: {meta_source:?}");
let doc_box: Box<dyn DocumentSource> = Box::new(doc);
let path = Path::new(pdf_path);
let sidecar_format = sidecar_format.to_string();
let native_options = eframe::NativeOptions {
viewport: dais_ui::display_mode::with_app_icon(egui::ViewportBuilder::default())
.with_title("Dais — Grouping Editor")
.with_inner_size(egui::vec2(1200.0, 320.0)),
..Default::default()
};
eframe::run_native(
"Dais Grouping Editor",
native_options,
Box::new(move |_cc| {
Ok(Box::new(dais_ui::grouping_editor::GroupingEditor::new(
doc_box,
path,
metadata,
&sidecar_format,
)))
}),
)
.map_err(|e| anyhow::anyhow!("eframe error: {e}"))?;
Ok(())
}
fn run_test_input(cli: &Cli) -> anyhow::Result<()> {
use dais_core::keybindings::KeybindingMap;
tracing::info!("Opening test-input diagnostic mode");
let config = if let Some(ref pdf_path) = cli.pdf_path {
let explicit_config = cli.config.as_deref().map(Path::new);
dais_core::config::load_config_for(Path::new(pdf_path), explicit_config)
} else if let Some(ref config_path) = cli.config {
dais_core::config::load_config_for(Path::new("."), Some(Path::new(config_path)))
} else {
dais_core::config::Config::default()
};
let keybindings = KeybindingMap::from_full_config(&config);
let native_options = eframe::NativeOptions {
viewport: dais_ui::display_mode::with_app_icon(egui::ViewportBuilder::default())
.with_title("Dais — Test Input")
.with_inner_size(egui::vec2(600.0, 500.0)),
..Default::default()
};
eframe::run_native(
"Dais Test Input",
native_options,
Box::new(move |_cc| Ok(Box::new(dais_ui::test_input::TestInputApp::new(keybindings)))),
)
.map_err(|e| anyhow::anyhow!("eframe error: {e}"))?;
Ok(())
}