diff --git a/src/config.rs b/src/config.rs index b4a68bd..11240e0 100644 --- a/src/config.rs +++ b/src/config.rs @@ -14,17 +14,17 @@ pub struct Config { impl Config { pub fn read_or_create() -> eyre::Result { let default_config_str = include_str!("../default-config.toml"); - let default_config = toml::from_str(default_config_str)?; + let mut config: Config = toml::from_str(default_config_str)?; let config_path = Path::new("config.toml"); if config_path.exists() { - let mut given_config = toml::from_str::(&fs::read_to_string(config_path)?)?; - given_config.update(default_config); - Ok(given_config) + let given_config = toml::from_str::(&fs::read_to_string(config_path)?)?; + config.update(given_config); + Ok(config) } else { println!("No config found, creating one at {config_path:?}"); fs::write(config_path, default_config_str)?; - Ok(default_config) + Ok(config) } } diff --git a/src/engines/answer.rs b/src/engines/answer.rs index 3d60649..dff5b48 100644 --- a/src/engines/answer.rs +++ b/src/engines/answer.rs @@ -1,6 +1,7 @@ pub mod dictionary; pub mod fend; pub mod ip; +pub mod notepad; pub mod numbat; pub mod thesaurus; pub mod timezone; diff --git a/src/engines/answer/notepad.rs b/src/engines/answer/notepad.rs new file mode 100644 index 0000000..0e543db --- /dev/null +++ b/src/engines/answer/notepad.rs @@ -0,0 +1,13 @@ +use crate::engines::{EngineResponse, SearchQuery}; + +use super::regex; + +pub fn request(query: &SearchQuery) -> EngineResponse { + if !regex!("(note|text|code) ?(pad|book|edit(or|er)?)").is_match(&query.query.to_lowercase()) { + return EngineResponse::new(); + } + + EngineResponse::answer_html( + r#"
"#.to_string() + ) +} diff --git a/src/engines/mod.rs b/src/engines/mod.rs index 3aaaf6d..9cdf41d 100644 --- a/src/engines/mod.rs +++ b/src/engines/mod.rs @@ -38,6 +38,7 @@ engines! { Dictionary = "dictionary", Fend = "fend", Ip = "ip", + Notepad = "notepad", Numbat = "numbat", Thesaurus = "thesaurus", Timezone = "timezone", @@ -46,6 +47,7 @@ engines! { // post-search DocsRs = "docs_rs", GitHub = "github", + Mdn = "mdn", StackExchange = "stackexchange", } @@ -63,6 +65,7 @@ engine_requests! { Dictionary => answer::dictionary::request, parse_response, Fend => answer::fend::request, None, Ip => answer::ip::request, None, + Notepad => answer::notepad::request, None, Numbat => answer::numbat::request, None, Thesaurus => answer::thesaurus::request, parse_response, Timezone => answer::timezone::request, None, @@ -77,9 +80,10 @@ engine_autocomplete_requests! { } engine_postsearch_requests! { - StackExchange => postsearch::stackexchange::request, parse_response, - GitHub => postsearch::github::request, parse_response, DocsRs => postsearch::docs_rs::request, parse_response, + GitHub => postsearch::github::request, parse_response, + Mdn => postsearch::mdn::request, parse_response, + StackExchange => postsearch::stackexchange::request, parse_response, } impl fmt::Display for Engine { diff --git a/src/engines/postsearch.rs b/src/engines/postsearch.rs index d0f4983..ecebba2 100644 --- a/src/engines/postsearch.rs +++ b/src/engines/postsearch.rs @@ -4,4 +4,5 @@ pub mod docs_rs; pub mod github; +pub mod mdn; pub mod stackexchange; diff --git a/src/engines/postsearch/mdn.rs b/src/engines/postsearch/mdn.rs new file mode 100644 index 0000000..f391656 --- /dev/null +++ b/src/engines/postsearch/mdn.rs @@ -0,0 +1,54 @@ +use scraper::{Html, Selector}; + +use crate::engines::{HttpResponse, Response, CLIENT}; + +pub fn request(response: &Response) -> Option { + for search_result in response.search_results.iter().take(8) { + if search_result + .url + .starts_with("https://developer.mozilla.org/en-US/docs/Web") + { + return Some(CLIENT.get(search_result.url.as_str())); + } + } + + None +} + +pub fn parse_response(HttpResponse { res, body }: &HttpResponse) -> Option { + let url = res.url().clone(); + + let dom = Html::parse_document(body); + + let page_title = dom + .select(&Selector::parse("header > h1").unwrap()) + .next()? + .text() + .collect::() + .trim() + .to_string(); + + let doc_query = Selector::parse(".section-content").unwrap(); + + let doc_html = dom + .select(&doc_query) + .next() + .map(|doc| doc.inner_html()) + .unwrap_or_default(); + + let doc_html = ammonia::Builder::default() + .link_rel(None) + .url_relative(ammonia::UrlRelative::RewriteWithBase(url.clone())) + .clean(&doc_html) + .to_string(); + + let title_html = format!( + r#"

{title}

"#, + url = html_escape::encode_quoted_attribute(&url.to_string()), + title = html_escape::encode_safe(&page_title), + ); + + Some(format!( + r#"{title_html}
{doc_html}
"# + )) +} diff --git a/src/web/assets/script.js b/src/web/assets/script.js index 5156ebb..c08d94d 100644 --- a/src/web/assets/script.js +++ b/src/web/assets/script.js @@ -109,6 +109,15 @@ document.addEventListener("keydown", (e) => { return; } + // if the currently selected element is not the search bar and is contenteditable, don't do anything + const focusedEl = document.querySelector(":focus"); + if ( + focusedEl && + (focusedEl.tagName == "input" || + focusedEl.getAttribute("contenteditable") !== null) + ) + return; + // if the user starts typing but they don't have focus on the input, focus it // no modifier keys