Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions apps/browser/assets/about-newtab.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
.about-newtab {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background: #f0f0f0;
}
.about-newtab .container {
display: flex;
flex-direction: column;
align-items: center;
gap: 24px;
}
.about-newtab img {
width: 160px;
height: 160px;
}
.about-newtab .search-input {
width: 320px;
padding: 10px 14px;
font-size: 16px;
border: 1px solid #ccc;
border-radius: 8px;
outline: none;
}
.about-newtab .search-input:focus {
border-color: #5e9ed6;
}
13 changes: 13 additions & 0 deletions apps/browser/assets/about-stub.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.about-stub {
height: 100%;
background: #f0f0f0;
color: #333;
padding: 40px;
}
.about-stub h1 {
margin: 0 0 8px;
font-size: 28px;
}
.about-stub p {
color: #888;
}
9 changes: 8 additions & 1 deletion apps/browser/assets/browser.css
Original file line number Diff line number Diff line change
Expand Up @@ -241,12 +241,19 @@ body,
width: 16px;
}

.menu-item-separator {
height: 1px;
background: #e8e8e8;
margin: 4px 0;
}

.webview {

.tab-content {
flex: 1 1 0px;
height: 100%;
width: 100%;
background: white;
font-family: sans-serif;
}

.fps-overlay {
Expand Down
26 changes: 0 additions & 26 deletions apps/browser/assets/start.html

This file was deleted.

152 changes: 152 additions & 0 deletions apps/browser/src/about_pages.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
use blitz_traits::net::{Request, Url};
use dioxus_native::prelude::*;

use crate::nav::{is_enter_key, req_from_string};

const NEWTAB_CSS: Asset = asset!("../assets/about-newtab.css");
const STUB_CSS: Asset = asset!("../assets/about-stub.css");
const BLITZ_LOGO: Asset = asset!("../assets/blitz-logo.png");

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum AboutPage {
NewTab,
Settings,
History,
Bookmarks,
}

impl AboutPage {
pub fn from_url(url: &Url) -> Option<Self> {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: try_from_url would probably be a more idiomatic name as this is fallible and returns option.

if url.scheme() != "about" {
return None;
}
match url.path() {
"newtab" | "" => Some(Self::NewTab),
"settings" => Some(Self::Settings),
"history" => Some(Self::History),
"bookmarks" => Some(Self::Bookmarks),
_ => None,
}
}

pub const fn title(self) -> &'static str {
match self {
Self::NewTab => "New Tab",
Self::Settings => "Settings",
Self::History => "History",
Self::Bookmarks => "Bookmarks",
}
}

pub const fn url_str(self) -> &'static str {
match self {
Self::NewTab => "about:newtab",
Self::Settings => "about:settings",
Self::History => "about:history",
Self::Bookmarks => "about:bookmarks",
}
}

#[allow(
clippy::unwrap_used,
reason = "URL strings are static literals — Url::parse cannot fail"
)]
pub fn parsed_url(self) -> Url {
Url::parse(self.url_str()).unwrap()
}
}

#[component]
pub fn AboutPageView(page: AboutPage, on_navigate: Callback<Request>) -> Element {
match page {
AboutPage::NewTab => rsx!(NewTabPage { on_navigate }),
AboutPage::Settings => rsx!(StubPage { name: "Settings" }),
AboutPage::History => rsx!(StubPage { name: "History" }),
AboutPage::Bookmarks => rsx!(StubPage { name: "Bookmarks" }),
}
}

#[component]
fn NewTabPage(on_navigate: Callback<Request>) -> Element {
let mut query = use_signal(String::new);
rsx! {
document::Link { rel: "stylesheet", href: NEWTAB_CSS }
div { class: "about-newtab",
div { class: "container",
img { src: BLITZ_LOGO, alt: "Blitz" }
input {
class: "search-input",
r#type: "text",
name: "q",
autofocus: true,
value: "{query}",
oninput: move |evt| query.set(evt.value()),
onkeydown: move |evt| {
if is_enter_key(&evt.key()) {
evt.prevent_default();
let q = query.read().clone();
if !q.is_empty() {
if let Some(req) = req_from_string(&q) {
on_navigate(req);
}
}
}
},
}
}
}
}
}

#[component]
fn StubPage(name: &'static str) -> Element {
rsx! {
document::Link { rel: "stylesheet", href: STUB_CSS }
div { class: "about-stub",
h1 { "{name}" }
p { "Coming soon." }
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn parses_known_paths() {
for (path, expected) in [
("about:newtab", AboutPage::NewTab),
("about:settings", AboutPage::Settings),
("about:history", AboutPage::History),
("about:bookmarks", AboutPage::Bookmarks),
] {
let url = Url::parse(path).unwrap();
assert_eq!(AboutPage::from_url(&url), Some(expected), "{path}");
}
}

#[test]
fn rejects_unknown() {
assert_eq!(
AboutPage::from_url(&Url::parse("about:nope").unwrap()),
None
);
assert_eq!(
AboutPage::from_url(&Url::parse("https://example.com").unwrap()),
None
);
}

#[test]
fn url_roundtrip() {
for p in [
AboutPage::NewTab,
AboutPage::Settings,
AboutPage::History,
AboutPage::Bookmarks,
] {
assert_eq!(AboutPage::from_url(&p.parsed_url()), Some(p));
}
}
}
16 changes: 0 additions & 16 deletions apps/browser/src/document_loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,22 +74,6 @@ impl DocumentLoader {
}

pub async fn load_document(&self, req: Request) -> LoadedDocument {
if req.url.scheme() == "about" && req.url.path() == "newtab" {
let config = make_doc_config(
None,
Arc::clone(&self.net_provider),
self.history,
self.font_ctx.clone(),
);
let html = include_str!("../assets/start.html");
let document = HtmlDocument::from_html(html, config).into_inner();
return LoadedDocument {
document: SubDocumentAttr::new(document),
html_source: html.to_string(),
title: String::new(),
};
}

let net_provider = Arc::clone(&self.net_provider);
let font_ctx = self.font_ctx.clone();
let history = self.history;
Expand Down
6 changes: 4 additions & 2 deletions apps/browser/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,28 @@ use winit::platform::macos::WindowAttributesMacOS;

pub(crate) type StdNetProvider = blitz_net::Provider;

mod about_pages;
#[cfg(any(feature = "screenshot", feature = "capture"))]
mod capture;
mod document_loader;
#[cfg(feature = "vello")]
mod fps_overlay;
mod history;
mod icons;
mod nav;
mod status_bar;
mod tab;
mod tab_strip;
mod toolbar;

use about_pages::AboutPage;
use status_bar::StatusBar;
use tab::{Tab, TabId, TabStoreImplExt, TabWebView, active_tab, open_tab, tab_title_or_url};
use tab_strip::TabStrip;
use toolbar::Toolbar;

static BROWSER_UI_STYLES: Asset = asset!("../assets/browser.css");
pub(crate) const IS_MOBILE: bool = cfg!(any(target_os = "android", target_os = "ios"));
pub(crate) const HOME_URL_STR: &str = "about:newtab";

#[unsafe(no_mangle)]
#[cfg(target_os = "android")]
Expand All @@ -63,7 +65,7 @@ fn main() {
}

fn app() -> Element {
let home_url = use_hook(|| Url::parse(HOME_URL_STR).unwrap());
let home_url = use_hook(|| AboutPage::NewTab.parsed_url());
let net_provider = use_context::<Arc<StdNetProvider>>();

let url_input_handle: Signal<Option<NodeHandle>> = use_signal(|| None);
Expand Down
45 changes: 45 additions & 0 deletions apps/browser/src/nav.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use blitz_traits::navigation::NavigationOptions;
use blitz_traits::net::{Body, Entry, EntryValue, FormData, Method, Request, Url};
use dioxus_native::prelude::Key;

pub fn req_from_string(url_s: &str) -> Option<Request> {
if let Ok(url) = Url::parse(url_s) {
return Some(Request::get(url));
};

let contains_space = url_s.contains(' ');
let contains_dot = url_s.contains('.');
if contains_dot && !contains_space {
if let Ok(url) = Url::parse(&format!("https://{}", &url_s)) {
return Some(Request::get(url));
}
}

Some(synthesize_duckduckgo_search_req(url_s))
}

fn synthesize_duckduckgo_search_req(query: &str) -> Request {
NavigationOptions::new(
Url::parse("https://html.duckduckgo.com/html/").unwrap(),
Some(String::from("application/x-www-form-urlencoded")),
0,
)
.set_method(Method::POST)
.set_document_resource(Body::Form(FormData(vec![Entry {
name: String::from("q"),
value: EntryValue::String(query.to_string()),
}])))
.into_request()
}

pub fn open_in_external_browser(req: &Request) {
if req.method == Method::GET && matches!(req.url.scheme(), "http" | "https" | "mailto") {
if let Err(err) = webbrowser::open(req.url.as_str()) {
tracing::error!("Failed to open URL: {}", err);
}
}
}

pub fn is_enter_key(key: &Key) -> bool {
matches!(key, Key::Enter) || matches!(key, Key::Character(s) if s == "\n")
}
Loading
Loading