1
0
mirror of https://github.com/Tim-Paik/neutauri.git synced 2024-10-12 23:29:41 +00:00

Compare commits

17 Commits

Author SHA1 Message Date
9f86775ea6 test drone
All checks were successful
continuous-integration/drone/push Build is passing
2022-05-02 20:55:49 +08:00
be6fc985e3 test drone
Some checks failed
continuous-integration/drone/push Build is failing
2022-05-02 20:52:40 +08:00
57102cd889 test drone
Some checks failed
continuous-integration/drone/push Build is failing
2022-05-02 19:25:42 +08:00
7a9cddf08a test drone
Some checks failed
continuous-integration/drone/push Build is failing
2022-05-02 19:10:57 +08:00
345e4bbca4 test drone
Some checks failed
continuous-integration/drone/push Build is failing
2022-05-02 16:23:39 +08:00
27ba6b6215 Fixed the problem of unsuccessful compilation under Linux 2022-05-02 15:56:39 +08:00
93eb7bd1dc Now the icon property can modify the icon of the windows exe file 2022-05-01 19:59:22 +08:00
15e5cde7e9 try using anyhow 2022-05-01 18:15:59 +08:00
dd6cd6b85e Added manifest option 2022-05-01 17:53:29 +08:00
b7408124e6 update bundler exe resources 2022-05-01 17:19:55 +08:00
084fe40035 Rename window_icon to icon 2022-05-01 16:55:49 +08:00
f3e9f6e936 fix: initialization_script now reads the same way as html 2022-05-01 16:43:11 +08:00
663f0159f4 Ready to integrate rcedit on windows 2022-05-01 16:41:32 +08:00
f3b45d106d fix: Parent folders are now automatically created when exporting artifacts 2022-05-01 13:58:32 +08:00
7555958627 fix: initialization_script now read the corresponding file instead of directly executing 2022-05-01 00:39:49 +08:00
5217d374aa fix clippy 2022-05-01 00:19:09 +08:00
638ba99d04 update deps ,dev mode now available 2022-04-30 23:05:43 +08:00
12 changed files with 973 additions and 470 deletions

30
.drone.yml Normal file
View File

@ -0,0 +1,30 @@
kind: pipeline
type: docker
name: default
steps:
- name: build
image: ubuntu:latest
commands:
- echo '========Install the necessary environment========'
- apt update && apt install -y curl gcc git libwebkit2gtk-4.0-dev libappindicator3-dev
- echo '========Install the Rust toolchain========'
- curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain stable -y
- echo '========Compile the Neutauri binary========'
- $HOME/.cargo/bin/cargo build --release --bin neutauri_runtime
- $HOME/.cargo/bin/cargo build --release --bin neutauri_bundler
- name: gitea_release
image: plugins/gitea-release
settings:
api_key:
from_secret: gitea_token
base_url: https://git.186526.xyz
files:
- ./target/release/neutauri_bundler
checksum:
- md5
- sha256
when:
event:
- tag

1242
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,3 @@ members = [
"neutauri_runtime",
"neutauri_bundler",
]
[neutauri_bundler.profile.release.package.wry]
debug = true
debug-assertions = true

View File

@ -4,11 +4,24 @@ name = "neutauri_bundler"
version = "0.1.0"
[dependencies]
anyhow = "1.0"
bincode = "1.3"
brotli = "3.3"
gumdrop = "0.8"
image = "0.23"
image = "0.24"
new_mime_guess = "4.0"
serde = {version = "1.0", features = ["derive"]}
toml = "0.5"
wry = "0.12"
wry = {version = "0.15", default-features = false, features = ["protocol", "tray", "gtk-tray", "transparent", "fullscreen", "devtools"]}
[target.'cfg(windows)'.dependencies]
rcedit = {git = "https://github.com/Tim-Paik/rcedit-rs.git", rev = "2805fca"}
[target.'cfg(windows)'.build-dependencies]
winres = "0.1"
[package.metadata.winres]
FileDescription = "Neutauri Bundler"
LegalCopyright = "@2022 Neutauri Developers"
OriginalFilename = ""
ProductName = "Neutauri"

12
neutauri_bundler/build.rs Normal file
View File

@ -0,0 +1,12 @@
#[cfg(windows)]
extern crate winres;
#[cfg(windows)]
fn main() {
let mut res = winres::WindowsResource::new();
res.set_icon("../neutauri_runtime/wry.ico");
res.compile().unwrap();
}
#[cfg(unix)]
fn main() {}

View File

@ -1,10 +1,16 @@
use crate::data;
use std::{fs, io::Write};
use std::{
fs,
io::{self, Write},
};
#[cfg(windows)]
const RUNTIME_DATA: &[u8] = include_bytes!("../../target/release/neutauri_runtime.exe");
#[cfg(not(windows))]
const RUNTIME_DATA: &[u8] = include_bytes!("../../target/release/neutauri_runtime");
use anyhow::Context;
#[cfg(windows)]
use std::{
env,
hash::{Hash, Hasher},
};
fn options() -> fs::OpenOptions {
#[cfg(not(windows))]
@ -18,10 +24,48 @@ fn options() -> fs::OpenOptions {
options
}
pub fn bundle(config_path: String) -> std::io::Result<()> {
#[cfg(not(windows))]
fn get_runtime_data(
_icon_path: Option<std::path::PathBuf>,
_manifest_path: Option<std::path::PathBuf>,
) -> anyhow::Result<Vec<u8>> {
Ok(include_bytes!("../../target/release/neutauri_runtime").to_vec())
}
#[cfg(windows)]
fn get_runtime_data(
icon_path: Option<std::path::PathBuf>,
manifest_path: Option<std::path::PathBuf>,
) -> anyhow::Result<Vec<u8>> {
let mut hasher = std::collections::hash_map::DefaultHasher::new();
hasher.write(b"neutauri_runtime");
std::time::SystemTime::now().hash(&mut hasher);
let temp_path = env::temp_dir().join(format!("{:x}.exe", hasher.finish()));
fs::write(
&temp_path,
include_bytes!("../../target/release/neutauri_runtime.exe"),
)?;
let mut updater = rcedit::ResourceUpdater::new();
updater.load(&temp_path)?;
if let Some(icon_path) = icon_path {
println!("{:?}", fs::canonicalize(&icon_path)?);
updater.set_icon(&fs::canonicalize(icon_path)?)?;
}
if let Some(manifest_path) = manifest_path {
updater.set_application_manifest(&fs::canonicalize(manifest_path)?)?;
}
updater.commit()?;
drop(updater);
let runtime_data =
fs::read(&temp_path).with_context(|| format!("Failed to read {}", temp_path.display()))?;
fs::remove_file(&temp_path)?;
Ok(runtime_data)
}
pub fn bundle(config_path: String) -> anyhow::Result<()> {
let config_path = std::path::Path::new(&config_path).canonicalize()?;
let config: data::Config = toml::from_str(fs::read_to_string(&config_path)?.as_str())
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
let source = match config_path.parent() {
Some(path) => path.join(&config.source).canonicalize()?,
None => config.source.canonicalize()?,
@ -30,6 +74,7 @@ pub fn bundle(config_path: String) -> std::io::Result<()> {
Some(path) => data::normalize_path(&path.join(&config.target)),
None => data::normalize_path(&config.target),
};
fs::create_dir_all(target.parent().unwrap_or_else(|| std::path::Path::new("/")))?;
let target = if target.extension() == None && cfg!(windows) {
target.with_extension("exe")
} else {
@ -41,7 +86,7 @@ pub fn bundle(config_path: String) -> std::io::Result<()> {
}
let data = data::Data::build_from_dir(source, config.window_attr()?, config.webview_attr()?)?;
let mut f = options().open(&target)?;
f.write_all(RUNTIME_DATA)?;
f.write_all(&get_runtime_data(config.icon, config.manifest)?)?;
f.write_all(&data)?;
f.sync_all()?;
f.flush()?;

View File

@ -62,11 +62,12 @@ pub struct Config {
pub transparent: bool,
pub decorations: bool,
pub always_on_top: bool,
pub window_icon: Option<PathBuf>,
pub icon: Option<PathBuf>,
pub spa: bool,
pub url: Option<String>,
pub html: Option<PathBuf>,
pub initialization_script: Option<String>,
pub initialization_script: Option<PathBuf>,
pub manifest: Option<PathBuf>,
}
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
@ -90,7 +91,7 @@ pub struct WindowAttr {
pub transparent: bool,
pub decorations: bool,
pub always_on_top: bool,
pub window_icon: Option<Icon>,
pub icon: Option<Icon>,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
@ -244,11 +245,12 @@ impl Default for Config {
transparent: false,
decorations: true,
always_on_top: false,
window_icon: None,
icon: None,
spa: false,
url: Some("/index.html".into()),
html: None,
initialization_script: Some("".into()),
initialization_script: None,
manifest: None,
}
}
}
@ -268,7 +270,7 @@ impl Config {
transparent: self.transparent,
decorations: self.decorations,
always_on_top: self.always_on_top,
window_icon: match &self.window_icon {
icon: match &self.icon {
Some(path) => Some(load_icon(path.as_path())?),
None => None,
},
@ -284,7 +286,10 @@ impl Config {
Some(path) => fs::read_to_string(path.as_path()).ok(),
None => None,
},
initialization_script: self.initialization_script.clone(),
initialization_script: match &self.initialization_script {
Some(path) => fs::read_to_string(path.as_path()).ok(),
None => None,
},
})
}
}

View File

@ -7,7 +7,7 @@ use wry::{
event_loop::{ControlFlow, EventLoop},
window::{Fullscreen, Icon, Window, WindowBuilder},
},
webview::{RpcRequest, WebContext, WebViewBuilder},
webview::{WebContext, WebViewBuilder},
};
use crate::data;
@ -35,7 +35,7 @@ pub fn dev(config_path: String) -> wry::Result<()> {
let config_path = std::path::Path::new(&config_path).canonicalize()?;
let config: data::Config = toml::from_str(fs::read_to_string(&config_path)?.as_str())
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
let source = config.source.clone().canonicalize()?;
let source = config.source.canonicalize()?;
let event_loop = EventLoop::new();
@ -43,7 +43,7 @@ pub fn dev(config_path: String) -> wry::Result<()> {
.with_always_on_top(config.window_attr()?.always_on_top)
.with_decorations(config.window_attr()?.decorations)
.with_resizable(config.window_attr()?.resizable)
.with_title(config.window_attr()?.title.clone())
.with_title(config.window_attr()?.title)
.with_maximized(config.window_attr()?.maximized)
.with_transparent(config.window_attr()?.transparent)
.with_visible(config.window_attr()?.visible);
@ -51,7 +51,7 @@ pub fn dev(config_path: String) -> wry::Result<()> {
true => window_builder.with_fullscreen(Some(Fullscreen::Borderless(None))),
false => window_builder,
};
let window_builder = match config.window_attr()?.window_icon {
let window_builder = match config.window_attr()?.icon {
Some(ref icon) => window_builder.with_window_icon(Some(Icon::from_rgba(
icon.rgba.clone(),
icon.width,
@ -83,7 +83,7 @@ pub fn dev(config_path: String) -> wry::Result<()> {
let window = window_builder.build(&event_loop)?;
let webview_builder = WebViewBuilder::new(window)?;
let url = config.webview_attr()?.url.clone();
let url = config.webview_attr()?.url;
let webview_builder = match url {
Some(url) => {
if url.starts_with('/') {
@ -94,12 +94,12 @@ pub fn dev(config_path: String) -> wry::Result<()> {
}
None => webview_builder.with_url(&custom_protocol_uri(PROTOCOL, "/index.html"))?,
};
let html = config.webview_attr()?.html.clone();
let html = config.webview_attr()?.html;
let webview_builder = match html {
Some(html) => webview_builder.with_html(&html)?,
None => webview_builder,
};
let initialization_script = config.webview_attr()?.initialization_script.clone();
let initialization_script = config.webview_attr()?.initialization_script;
let webview_builder = match initialization_script {
Some(script) => webview_builder.with_initialization_script(&script),
None => webview_builder,
@ -109,7 +109,7 @@ pub fn dev(config_path: String) -> wry::Result<()> {
false => webview_builder
.with_visible(false)
.with_initialization_script(
r#"window.addEventListener('load', function(event) { rpc.call('show_window'); });"#,
r#"window.addEventListener('load', function(event) { window.ipc.postMessage('show_window'); });"#,
),
};
let path = std::env::current_exe()?;
@ -148,7 +148,7 @@ pub fn dev(config_path: String) -> wry::Result<()> {
.with_custom_protocol(PROTOCOL.to_string(), move |request| {
let path = custom_protocol_uri_to_path(PROTOCOL, request.uri())?;
let mut local_path = source.clone();
local_path.push(path.strip_prefix("/").unwrap_or_else(|| &path));
local_path.push(path.strip_prefix('/').unwrap_or(&path));
let mut data = Vec::new();
let mut mime: String = "application/octet-stream".to_string();
match fs::File::open(&local_path) {
@ -172,14 +172,14 @@ pub fn dev(config_path: String) -> wry::Result<()> {
}
wry::http::ResponseBuilder::new().mimetype(&mime).body(data)
})
.with_rpc_handler(|window: &Window, req: RpcRequest| {
match req.method.as_str() {
.with_ipc_handler(|window: &Window, req: String| {
match req.as_str() {
"show_window" => window.set_visible(true),
"ping" => println!("recived a ping"),
_ => (),
};
None
})
.with_devtools(true)
.build()?;
event_loop.run(move |event, _, control_flow| {

View File

@ -48,7 +48,7 @@ fn print_help_and_exit(args: Args) {
"Usage: {:?} [SUBCOMMAND] [OPTIONS]",
std::env::args()
.into_iter()
.nth(0)
.next()
.unwrap_or_else(|| "neutauri_bundler".to_string())
);
eprintln!();
@ -59,7 +59,7 @@ fn print_help_and_exit(args: Args) {
std::process::exit(0);
}
fn main() -> wry::Result<()> {
fn main() -> anyhow::Result<()> {
let args = std::env::args().collect::<Vec<_>>();
let args = Args::parse_args(&args[1..], gumdrop::ParsingStyle::default()).unwrap_or_else(|e| {
eprintln!("{}: {}", args[0], e);

View File

@ -7,7 +7,15 @@ version = "0.1.0"
bincode = "1.3"
brotli = "3.3"
serde = {version = "1.0", features = ["derive"]}
wry = "0.12"
wry = {version = "0.15", default-features = false, features = ["protocol", "tray", "gtk-tray", "transparent", "fullscreen"]}
[target.'cfg(windows)'.build-dependencies]
winres = "0.1"
[package.metadata.winres]
FileDescription = ""
FileVersion = ""
LegalCopyright = ""
OriginalFilename = ""
ProductName = ""
ProductVersion = ""

View File

@ -62,11 +62,12 @@ pub struct Config {
pub transparent: bool,
pub decorations: bool,
pub always_on_top: bool,
pub window_icon: Option<PathBuf>,
pub icon: Option<PathBuf>,
pub spa: bool,
pub url: Option<String>,
pub html: Option<PathBuf>,
pub initialization_script: Option<String>,
pub manifest: Option<PathBuf>,
}
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
@ -90,7 +91,7 @@ pub struct WindowAttr {
pub transparent: bool,
pub decorations: bool,
pub always_on_top: bool,
pub window_icon: Option<Icon>,
pub icon: Option<Icon>,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
@ -212,11 +213,12 @@ impl Default for Config {
transparent: false,
decorations: true,
always_on_top: false,
window_icon: None,
icon: None,
spa: false,
url: Some("/index.html".into()),
html: None,
initialization_script: Some("".into()),
initialization_script: None,
manifest: None,
}
}
}

View File

@ -9,7 +9,7 @@ use wry::{
event_loop::{ControlFlow, EventLoop},
window::{Fullscreen, Icon, Window, WindowBuilder},
},
webview::{RpcRequest, WebContext, WebViewBuilder},
webview::{WebContext, WebViewBuilder},
};
mod data;
@ -51,7 +51,7 @@ fn main() -> wry::Result<()> {
true => window_builder.with_fullscreen(Some(Fullscreen::Borderless(None))),
false => window_builder,
};
let window_builder = match res.window_attr.window_icon {
let window_builder = match res.window_attr.icon {
Some(ref icon) => window_builder.with_window_icon(Some(Icon::from_rgba(
icon.rgba.clone(),
icon.width,
@ -109,7 +109,7 @@ fn main() -> wry::Result<()> {
false => webview_builder
.with_visible(false)
.with_initialization_script(
r#"window.addEventListener('load', function(event) { rpc.call('show_window'); });"#,
r#"window.addEventListener('load', function(event) { window.ipc.postMessage('show_window'); });"#,
),
};
let path = std::env::current_exe()?;
@ -161,14 +161,14 @@ fn main() -> wry::Result<()> {
.mimetype(&file.mimetype())
.body(file.decompressed_data()?)
})
.with_rpc_handler(|window: &Window, req: RpcRequest| {
match req.method.as_str() {
.with_ipc_handler(|window: &Window, req: String| {
match req.as_str() {
"show_window" => window.set_visible(true),
"ping" => println!("recived a ping"),
_ => (),
};
None
})
.with_devtools(false)
.build()?;
event_loop.run(move |event, _, control_flow| {