* work * moar work * notes * work * separate to terminal and control channels * stdin working * serve html web client initial * serve static assets loaded with include_dir * merge * enable_web_server config parameter * compile time flag to disable web server capability * rustfmt * add license to all xterm.js assets * mouse working except copy/paste * helpful comment * web client improvements - move script to js file - add favicon - add nerd font - change title TODO: investigate if font license embedded in otf is sufficient * get mouse to work properly * kitty keyboard support initial * fix wrong type in preload link * wip axum websocket handlers - upgrade axum to v0.8.1, enable ws feature - begin setup of websocket handlers - tidy up imports * replace control listener * handle terminal websocket with axum * cleanup Cargo.toml * kitty fixes and bracketed paste * fix(mouse): pane not found crash * initial session switching infra * add `web_client_font` option * session switching, creation and resurrection working through the session manager * move session module to zellij-utils and share logic with web-client * some cleanups * require restart for enable-web-server * use session name from router * write config to disk and watch for config changes * rename session name to ipc path * add basic panic handler, make render_to_client exit on channel close * use while let instead of loop * handle websocket close * add mouse motions * make clipboard work * add weblink handling and webgl rendering * add todo * fix: use session name instead of patch on session switch * use "default" layout for new sessions * ui indication for session being shared * share this session ui * plugin assets * Fix process crash on mac with notify watcher. Use poll watcher instead of recommended as a workaround. * make url session switching and creation work * start welcome screen on root url * scaffold control messages, set font from config * set dimensions on session start * bring back session name from url * send bytes on terminal websocket instead of json - create web client os input and id before websocket connection * draft ui * work * refactor ui * remove otf font, remove margins to avoid scrollbar * version query endpoint for server status * web session info query endpoint * refactor: move stuff around * add web client info to session metadata * make tests pass * populate real data in session list * remove unnecessary endpoint * add web_client node to config, add font option * remove web_client_font * allow disabling the web session through the config - WIP * formalize sharing/not-sharing configuration * fix tests * allow shutting down web server * display error when web clients are forbidden to attach * only show sessions that allow web clients if this is a web client * style(fmt): rustfmt * fix: query web server from Zellij rather than from each plugin * remove log spam * handle some error paths better in the web client * allow controlling the web server through the cli * allow configuring the web server's ip/port * fix tests and format code * use direct WebServerStatus event instead of piggy-backing on SessionInfo * plugin revamp initial * make plugin responsive * adjust plugin title * refactor: share plugin * refactor: share plugin * add cors middleware * some fixes for running without a compiled web server capability * display error when starting the share plugin without web server support * clarify config * add pipelines to compile zellij without web support * display error when unable to start web server * only query web server when share plugin is running * refactor(web-client): connection table * give zellij_server_listener access to the control channel * fixes and clarifications * refactor: consolidate generate_unique_session_name * give proper error when trying to attach to a forbidden session * change browser URL when switching sessions * add keyboard shortcut * enforce https when bound to non-loopback ip * initial authentication token implementation * background color from theme * initial web client theme config * basic token generation ui * refactor set config message creation * also set body background * allow editing scrollback for plugins too * set scrollback to 0 * properly parse colors in config * generate token from plugin * nice login modals * initial token management screen * implement token authentication * refactor(share): token management screen * style(fmt): rustfmt * fix(plugin): some minor bugs * refactor(share): main screen * refactor(share): token screen * refactor(share): main * refactor(share): ui components * fix(responsiveness): properly send usage_width to the render function * fix cli commands and add some verbosity * add support for settings ansi and selection colors * add cursor and cursor accent * basic web client tests * fix tests * refactor: web client * use session tokens for authentication * improve modals * move shutdown to ipc * refactor: ipc logic * serialize theme config for web client * update tests * refactor: move some stuff around to prepare for config hot reload * config live reloading for the web clients * change remember-me UI wording * improve xterm.js link handling * make sure terminal is focused on mousemove * remove deprecated sharing indication from compact-bar * gate deps and functionality behind the web_server_compatibility feature * feat(build): add --no-web flag in all the places * fix some other build flows * add new assets * update CI for no-web (untested) * make more dependencies optional * update axum-extra * add web client configuration options * gracefully close connections on server exit * tests for graceful connection closing * handle client-side reconnect when server is down * fix: make sure ipc bus folder exists before starting * add commands to manage login tokens from the cli * style(fmt): rustfmt * some cleanups * fix(ux): allow alt-right-click on the web client without opening the context menu * fix: prevent attaching to welcome screen * fix: reload config issues * fix long socket path on macos * normalize config conversion and fix color gap in browser * revoke session_token cookie if it is not valid * fix: visual bug with multiple clients in extremely small screen sizes * fix: only include rusqlite for the web server capability builds * update e2e snapshots * refactor(web): client side js * some cleanups * moar cleanups * fix(tests): wait for server instead of using a fixed timeout * debug CI * fix(tests): use spawn_blocking for running the test web server * fix(tests): wait for http rather than tcp port * fix(tests): properly pass config path - hopefully this is the issue... * success! bring back the rest of the tests * attempt to fix the macos CI issue * docs(changelog): add PR --------- Co-authored-by: Thomas Linford <linford.t@gmail.com>
192 lines
6.9 KiB
Rust
192 lines
6.9 KiB
Rust
//! Subcommands for building.
|
|
//!
|
|
//! Currently has the following functions:
|
|
//!
|
|
//! - [`build`]: Builds general cargo projects (i.e. zellij components) with `cargo build`
|
|
//! - [`manpage`]: Builds the manpage with `mandown`
|
|
use crate::{flags, metadata, WorkspaceMember};
|
|
use anyhow::Context;
|
|
use std::path::{Path, PathBuf};
|
|
use xshell::{cmd, Shell};
|
|
|
|
/// Build members of the zellij workspace.
|
|
///
|
|
/// Build behavior is controlled by the [`flags`](flags::Build). Calls some variation of `cargo
|
|
/// build` under the hood.
|
|
pub fn build(sh: &Shell, flags: flags::Build) -> anyhow::Result<()> {
|
|
let _pd = sh.push_dir(crate::project_root());
|
|
|
|
let cargo = crate::cargo()?;
|
|
if flags.no_plugins && flags.plugins_only {
|
|
eprintln!("Cannot use both '--no-plugins' and '--plugins-only'");
|
|
std::process::exit(1);
|
|
}
|
|
|
|
for WorkspaceMember { crate_name, .. } in crate::workspace_members()
|
|
.iter()
|
|
.filter(|member| member.build)
|
|
{
|
|
let err_context = || format!("failed to build '{crate_name}'");
|
|
|
|
if crate_name.contains("plugins") {
|
|
if flags.no_plugins {
|
|
continue;
|
|
}
|
|
} else if flags.plugins_only {
|
|
continue;
|
|
}
|
|
|
|
// zellij-utils requires protobuf definition files to be present. Usually these are
|
|
// auto-generated with `build.rs`-files, but this is currently broken for us.
|
|
// See [this PR][1] for details.
|
|
//
|
|
// [1]: https://github.com/zellij-org/zellij/pull/2711#issuecomment-1695015818
|
|
{
|
|
let zellij_utils_basedir = crate::project_root().join("zellij-utils");
|
|
let _pd = sh.push_dir(zellij_utils_basedir);
|
|
|
|
let prost_asset_dir = sh.current_dir().join("assets").join("prost");
|
|
let protobuf_source_dir = sh.current_dir().join("src").join("plugin_api");
|
|
std::fs::create_dir_all(&prost_asset_dir).unwrap();
|
|
|
|
let mut prost = prost_build::Config::new();
|
|
let last_generated = prost_asset_dir
|
|
.join("generated_plugin_api.rs")
|
|
.metadata()
|
|
.and_then(|m| m.modified());
|
|
let mut needs_regeneration = false;
|
|
prost.out_dir(prost_asset_dir);
|
|
prost.include_file("generated_plugin_api.rs");
|
|
let mut proto_files = vec![];
|
|
for entry in std::fs::read_dir(&protobuf_source_dir).unwrap() {
|
|
let entry_path = entry.unwrap().path();
|
|
if entry_path.is_file() {
|
|
if !entry_path
|
|
.extension()
|
|
.map(|e| e == "proto")
|
|
.unwrap_or(false)
|
|
{
|
|
continue;
|
|
}
|
|
proto_files.push(entry_path.display().to_string());
|
|
let modified = entry_path.metadata().and_then(|m| m.modified());
|
|
needs_regeneration |= match (&last_generated, modified) {
|
|
(Ok(last_generated), Ok(modified)) => modified >= *last_generated,
|
|
// Couldn't read some metadata, assume needs update
|
|
_ => true,
|
|
}
|
|
}
|
|
}
|
|
if needs_regeneration {
|
|
prost
|
|
.compile_protos(&proto_files, &[protobuf_source_dir])
|
|
.unwrap();
|
|
}
|
|
}
|
|
|
|
let _pd = sh.push_dir(Path::new(crate_name));
|
|
// Tell the user where we are now
|
|
println!();
|
|
let msg = format!(">> Building '{crate_name}'");
|
|
crate::status(&msg);
|
|
println!("{}", msg);
|
|
|
|
let mut base_cmd = cmd!(sh, "{cargo} build");
|
|
if flags.release {
|
|
base_cmd = base_cmd.arg("--release");
|
|
}
|
|
if flags.no_web {
|
|
// Check if this crate has web features that need modification
|
|
match metadata::get_no_web_features(sh, crate_name)
|
|
.context("Failed to check web features")?
|
|
{
|
|
Some(features) => {
|
|
base_cmd = base_cmd.arg("--no-default-features");
|
|
if !features.is_empty() {
|
|
base_cmd = base_cmd.arg("--features");
|
|
base_cmd = base_cmd.arg(features);
|
|
}
|
|
},
|
|
None => {
|
|
// Crate doesn't have web features, build normally
|
|
},
|
|
}
|
|
}
|
|
base_cmd.run().with_context(err_context)?;
|
|
|
|
if crate_name.contains("plugins") {
|
|
let (_, plugin_name) = crate_name
|
|
.rsplit_once('/')
|
|
.context("Cannot determine plugin name from '{subcrate}'")?;
|
|
|
|
if flags.release {
|
|
// Move plugin into assets folder
|
|
move_plugin_to_assets(sh, plugin_name)?;
|
|
}
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn move_plugin_to_assets(sh: &Shell, plugin_name: &str) -> anyhow::Result<()> {
|
|
let err_context = || format!("failed to move plugin '{plugin_name}' to assets folder");
|
|
|
|
// Get asset path
|
|
let asset_name = crate::asset_dir()
|
|
.join("plugins")
|
|
.join(plugin_name)
|
|
.with_extension("wasm");
|
|
|
|
// Get plugin path
|
|
let plugin = PathBuf::from(
|
|
std::env::var_os("CARGO_TARGET_DIR")
|
|
.unwrap_or(crate::project_root().join("target").into_os_string()),
|
|
)
|
|
.join("wasm32-wasip1")
|
|
.join("release")
|
|
.join(plugin_name)
|
|
.with_extension("wasm");
|
|
|
|
if !plugin.is_file() {
|
|
return Err(anyhow::anyhow!("No plugin found at '{}'", plugin.display()))
|
|
.with_context(err_context);
|
|
}
|
|
|
|
// This is a plugin we want to move
|
|
let from = plugin.as_path();
|
|
let to = asset_name.as_path();
|
|
sh.copy_file(from, to).with_context(err_context)
|
|
}
|
|
|
|
/// Build the manpage with `mandown`.
|
|
// mkdir -p ${root_dir}/assets/man
|
|
// mandown ${root_dir}/docs/MANPAGE.md 1 > ${root_dir}/assets/man/zellij.1
|
|
pub fn manpage(sh: &Shell) -> anyhow::Result<()> {
|
|
let err_context = "failed to generate manpage";
|
|
|
|
let mandown = mandown(sh).context(err_context)?;
|
|
|
|
let project_root = crate::project_root();
|
|
let asset_dir = &project_root.join("assets").join("man");
|
|
sh.create_dir(asset_dir).context(err_context)?;
|
|
let _pd = sh.push_dir(asset_dir);
|
|
|
|
cmd!(sh, "{mandown} {project_root}/docs/MANPAGE.md 1")
|
|
.read()
|
|
.and_then(|text| sh.write_file("zellij.1", text))
|
|
.context(err_context)
|
|
}
|
|
|
|
/// Get the path to a `mandown` executable.
|
|
///
|
|
/// If the executable isn't found, an error is returned instead.
|
|
fn mandown(_sh: &Shell) -> anyhow::Result<PathBuf> {
|
|
match which::which("mandown") {
|
|
Ok(path) => Ok(path),
|
|
Err(e) => {
|
|
eprintln!("!! 'mandown' wasn't found but is needed for this build step.");
|
|
eprintln!("!! Please install it with: `cargo install mandown`");
|
|
Err(e).context("Couldn't find 'mandown' executable")
|
|
},
|
|
}
|
|
}
|