Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 01e9fb4256 | |||
| f74ebb45e1 | |||
| 13a2e52f9d |
56
html/index.html
Normal file
56
html/index.html
Normal file
@@ -0,0 +1,56 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>SilverBullet</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<style>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
main {
|
||||
display: flex;
|
||||
margin: 0 auto;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 100vh;
|
||||
max-width: 768px;
|
||||
}
|
||||
|
||||
input, button {
|
||||
padding: 10px;
|
||||
margin: 5px 0;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<form id="set_url" target="#">
|
||||
<label for="url"><b>Set your SilverBullet instance</b></label>
|
||||
<input type="url" id="url" name="url" required autofocus />
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
</main>
|
||||
<script>
|
||||
const form = document.getElementById('set_url');
|
||||
form.addEventListener('submit', async (e) => {
|
||||
try {
|
||||
// Test that the URL is a valid URL.
|
||||
const url = new URL(form.elements['url'].value);
|
||||
// Test that the URL resolves to an actual website.
|
||||
await fetch(url.href, { method: 'HEAD', mode: 'no-cors' });
|
||||
window.ipc.postMessage(JSON.stringify({ command: 'set_url', url }));
|
||||
window.location.assign(url);
|
||||
} catch(e) {
|
||||
if (e instanceof TypeError) {
|
||||
// Fetch failed due to network error (dns, etc.)
|
||||
alert('Failed to connect to SilverBullet due to a network error.\nPlease check that the URL is correct and you are connected.');
|
||||
} else {
|
||||
alert('The SilverBullet URL doesn\'t seem to be valid.\nPlease check that the URL is correct.');
|
||||
}
|
||||
}
|
||||
e.preventDefault();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -11,7 +11,7 @@ window.addEventListener('load', () => {
|
||||
if (target.href !== null) {
|
||||
if (target.host !== window.location.host) {
|
||||
window.ipc.postMessage(JSON.stringify({
|
||||
command: "open",
|
||||
command: 'open',
|
||||
uri: target.href,
|
||||
}));
|
||||
e.preventDefault();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use eyre::eyre;
|
||||
use confy::ConfyError;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub const APP_NAME: &str = env!("CARGO_PKG_NAME");
|
||||
@@ -10,11 +10,11 @@ pub struct Config {
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn load() -> eyre::Result<Self> {
|
||||
let cfg: Config = confy::load(APP_NAME, "config")?;
|
||||
if cfg.base_url.is_empty() {
|
||||
return Err(eyre!("base_url must not be empty"));
|
||||
}
|
||||
Ok(cfg)
|
||||
pub fn load() -> Result<Self, ConfyError> {
|
||||
confy::load(APP_NAME, "config")
|
||||
}
|
||||
|
||||
pub fn store(&self) -> Result<(), ConfyError> {
|
||||
confy::store(APP_NAME, "config", self)
|
||||
}
|
||||
}
|
||||
|
||||
11
src/ipc.rs
11
src/ipc.rs
@@ -2,10 +2,13 @@ use std::process;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::config::Config;
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[serde(tag = "command", rename_all = "lowercase")]
|
||||
#[serde(tag = "command", rename_all = "snake_case")]
|
||||
pub enum Command {
|
||||
Open { uri: String },
|
||||
SetUrl { url: String },
|
||||
}
|
||||
|
||||
impl Command {
|
||||
@@ -15,6 +18,12 @@ impl Command {
|
||||
process::Command::new("xdg-open").arg(uri).output()?;
|
||||
Ok(())
|
||||
}
|
||||
Command::SetUrl { url } => {
|
||||
let cfg = Config {
|
||||
base_url: url.to_string(),
|
||||
};
|
||||
Ok(cfg.store()?)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,27 +15,34 @@ pub fn run(cfg: Config) -> eyre::Result<()> {
|
||||
.with_title("SilverBullet")
|
||||
.build(&event_loop)?;
|
||||
|
||||
let builder = WebViewBuilder::new_gtk(
|
||||
window
|
||||
.default_vbox()
|
||||
.ok_or_else(|| eyre!("failed to get vbox"))?,
|
||||
);
|
||||
let data_dir = dirs::state_dir()
|
||||
.ok_or_else(|| eyre!("failed to find state dir"))?
|
||||
.join(APP_NAME);
|
||||
let mut context = WebContext::new(Some(data_dir));
|
||||
|
||||
let webview = builder
|
||||
.with_initialization_script(include_str!("../js/init.js"))
|
||||
.with_ipc_handler(|msg| match serde_json::from_str::<Command>(&msg) {
|
||||
Ok(cmd) => cmd
|
||||
.handle()
|
||||
.unwrap_or_else(|err| eprintln!("failed to handle command: {err}")),
|
||||
Err(err) => eprintln!("invalid ipc command {msg}: {err}"),
|
||||
})
|
||||
.with_web_context(&mut context)
|
||||
.with_url(&cfg.base_url)?
|
||||
.build()?;
|
||||
let mut builder = WebViewBuilder::new_gtk(
|
||||
window
|
||||
.default_vbox()
|
||||
.ok_or_else(|| eyre!("failed to get vbox"))?,
|
||||
)
|
||||
.with_initialization_script(include_str!("../js/init.js"))
|
||||
.with_ipc_handler(|msg| match serde_json::from_str::<Command>(&msg) {
|
||||
Ok(cmd) => cmd
|
||||
.handle()
|
||||
.unwrap_or_else(|err| eprintln!("failed to handle command: {err}")),
|
||||
Err(err) => eprintln!("invalid ipc command {msg}: {err}"),
|
||||
})
|
||||
.with_web_context(&mut context);
|
||||
if cfg.base_url.is_empty() {
|
||||
builder = builder.with_html(include_str!("../html/index.html"))?;
|
||||
} else {
|
||||
let mut base_url = cfg.base_url;
|
||||
if !(base_url.starts_with("http://") || base_url.starts_with("https://")) {
|
||||
base_url.insert_str(0, "https://");
|
||||
}
|
||||
builder = builder.with_url(&base_url)?;
|
||||
}
|
||||
let webview = builder.build()?;
|
||||
|
||||
let mut modifiers = ModifiersState::default();
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
|
||||
Reference in New Issue
Block a user