mod assets;
pub mod image;
pub mod llm;
pub mod prompt_builder;
pub mod routes;
pub mod state;
pub mod storage;
pub mod tool_bridge;
use std::sync::Arc;
use anyhow::Result;
use crate::cli::UiArgs;
use state::AppState;
pub async fn launch(args: UiArgs) -> Result<()> {
use crate::cli::Provider;
let knowledge_base = Arc::new(crate::knowledge::Knowledge::embedded());
let system_prompt = prompt_builder::build_system_prompt(&knowledge_base, None);
let llm = match args.provider {
Provider::Openai => {
let api_key = args.openai_api_key.ok_or_else(|| {
anyhow::anyhow!(
"OpenAI API key required: set --openai-api-key or OPENAI_API_KEY env var"
)
})?;
let model = args.model.unwrap_or_else(|| "gpt-4o".to_string());
llm::LlmBackend::OpenAi(llm::openai::OpenAiProvider { api_key, model })
}
Provider::Anthropic => {
let api_key = args.anthropic_api_key.ok_or_else(|| {
anyhow::anyhow!(
"Anthropic API key required: set --anthropic-api-key or ANTHROPIC_API_KEY env var"
)
})?;
let model = args
.model
.unwrap_or_else(|| "claude-sonnet-4-20250514".to_string());
llm::LlmBackend::Anthropic(llm::anthropic::AnthropicProvider { api_key, model })
}
};
std::fs::create_dir_all(&args.storage_dir)?;
let image_store = storage::local::LocalImageStore::new(&args.storage_dir)?;
let state = Arc::new(AppState {
llm,
system_prompt,
knowledge_base,
image_store,
pack_jobs: tokio::sync::Mutex::new(std::collections::HashMap::new()),
wizard_jobs: tokio::sync::Mutex::new(std::collections::HashMap::new()),
});
let router = routes::build(state);
let addr = format!("127.0.0.1:{}", args.port.unwrap_or(0));
let listener = tokio::net::TcpListener::bind(&addr).await?;
let port = listener.local_addr()?.port();
let url = format!("http://127.0.0.1:{port}");
eprintln!("Greentic Designer UI started at: {url}");
let _ = open::that(&url);
axum::serve(listener, router).await?;
Ok(())
}