aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2026-02-15 12:55:22 +0100
committerGitHub <noreply@github.com>2026-02-15 12:55:22 +0100
commit753110669046196d94ffb1824f6c827a7497eaf0 (patch)
tree589a9515cdc0e37ef00446955503dbb001cc7c03
parentAllow plugins to read env vars present upon session creation (behind permissi... (diff)
downloadsl0ppij-753110669046196d94ffb1824f6c827a7497eaf0.tar.gz
sl0ppij-753110669046196d94ffb1824f6c827a7497eaf0.tar.xz
sl0ppij-753110669046196d94ffb1824f6c827a7497eaf0.zip
Fix startup message and cache file creation (#4705)
* only show startup loading message if we've been loading for more than 400ms * create relevant cache folders before attempting to write startup cache files * fix flaky tests * rustfmt * add pr
-rw-r--r--CHANGELOG.md1
-rw-r--r--zellij-client/src/lib.rs51
-rw-r--r--zellij-client/src/stdin_ansi_parser.rs3
-rw-r--r--zellij-server/src/lib.rs3
-rw-r--r--zellij-server/src/unit/screen_tests.rs21
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_send_keys_action_to_screen.snap19
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_active_tab_sync_action.snap4
-rw-r--r--zellij-utils/src/channels.rs3
8 files changed, 85 insertions, 20 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fb9ac53f..567a2b15 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -49,6 +49,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
* feat: allow querying tab info (id, name and position) from the CLI as well as general info about all tabs (https://github.com/zellij-org/zellij/pull/4695)
* feat: propagate plugin configuration changes to plugins (https://github.com/zellij-org/zellij/pull/4698)
* feat: allow plugins to query env vars set upon session creation (gated behind a relevant permission) (https://github.com/zellij-org/zellij/pull/4700)
+* fix: make sure cache folders exist in all startup paths (https://github.com/zellij-org/zellij/pull/4705)
## [0.43.1] - 2025-08-08
diff --git a/zellij-client/src/lib.rs b/zellij-client/src/lib.rs
index b5224eb4..4061b690 100644
--- a/zellij-client/src/lib.rs
+++ b/zellij-client/src/lib.rs
@@ -83,7 +83,7 @@ use crate::{
use termwiz::input::InputEvent;
use zellij_utils::cli::CliArgs;
use zellij_utils::{
- channels::{self, ChannelWithContext, SenderWithContext},
+ channels::{self, ChannelWithContext, RecvTimeoutError, SenderWithContext},
consts::{set_permissions, ZELLIJ_SOCK_DIR},
data::{ClientId, ConnectToSession, KeyWithModifier, LayoutInfo, LayoutMetadata},
envs,
@@ -947,6 +947,9 @@ pub fn start_client(
let mut exit_msg = String::new();
let mut loading = true;
+ let mut showed_loading_message = false;
+ let loading_start = std::time::Instant::now();
+ let loading_delay = std::time::Duration::from_millis(400);
let mut pending_instructions = vec![];
let mut synchronised_output = match os_input.env_variable("TERM").as_deref() {
Some("alacritty") => Some(SyncOutput::DCS),
@@ -954,16 +957,34 @@ pub fn start_client(
};
let mut stdout = os_input.get_stdout_writer();
- stdout
- .write_all("\u{1b}[1m\u{1b}[HLoading Zellij\u{1b}[m\n\r".as_bytes())
- .expect("cannot write to stdout");
- stdout.flush().expect("could not flush");
loop {
let (client_instruction, mut err_ctx) = if !loading && !pending_instructions.is_empty() {
// there are buffered instructions, we need to go through them before processing the
// new ones
pending_instructions.remove(0)
+ } else if loading && !showed_loading_message {
+ let remaining = loading_delay
+ .checked_sub(loading_start.elapsed())
+ .unwrap_or(std::time::Duration::ZERO);
+ match receive_client_instructions.recv_timeout(remaining) {
+ Ok(instruction) => instruction,
+ Err(RecvTimeoutError::Timeout) => {
+ // 400ms elapsed with no completion — show loading UI
+ stdout
+ .write_all("\u{1b}[1m\u{1b}[HLoading Zellij\u{1b}[m\n\r".as_bytes())
+ .expect("cannot write to stdout");
+ stdout.flush().expect("could not flush");
+ showed_loading_message = true;
+ // Now block normally
+ receive_client_instructions
+ .recv()
+ .expect("failed to receive app instruction on channel")
+ },
+ Err(RecvTimeoutError::Disconnected) => {
+ panic!("client instruction channel disconnected");
+ },
+ }
} else {
receive_client_instructions
.recv()
@@ -974,16 +995,20 @@ pub fn start_client(
// when the app is still loading, we buffer instructions and show a loading screen
match client_instruction {
ClientInstruction::StartedParsingStdinQuery => {
- stdout
- .write_all("Querying terminal emulator for \u{1b}[32;1mdefault colors\u{1b}[m and \u{1b}[32;1mpixel/cell\u{1b}[m ratio...".as_bytes())
- .expect("cannot write to stdout");
- stdout.flush().expect("could not flush");
+ if showed_loading_message {
+ stdout
+ .write_all("Querying terminal emulator for \u{1b}[32;1mdefault colors\u{1b}[m and \u{1b}[32;1mpixel/cell\u{1b}[m ratio...".as_bytes())
+ .expect("cannot write to stdout");
+ stdout.flush().expect("could not flush");
+ }
},
ClientInstruction::DoneParsingStdinQuery => {
- stdout
- .write_all("done".as_bytes())
- .expect("cannot write to stdout");
- stdout.flush().expect("could not flush");
+ if showed_loading_message {
+ stdout
+ .write_all("done".as_bytes())
+ .expect("cannot write to stdout");
+ stdout.flush().expect("could not flush");
+ }
loading = false;
},
instruction => {
diff --git a/zellij-client/src/stdin_ansi_parser.rs b/zellij-client/src/stdin_ansi_parser.rs
index 7fa3492e..310360e3 100644
--- a/zellij-client/src/stdin_ansi_parser.rs
+++ b/zellij-client/src/stdin_ansi_parser.rs
@@ -129,6 +129,9 @@ impl StdinAnsiParser {
}
pub fn write_cache(&self, events: Vec<AnsiStdinInstruction>) {
if let Ok(serialized_events) = serde_json::to_string(&events) {
+ if let Some(parent) = ZELLIJ_STDIN_CACHE_FILE.parent() {
+ let _ = std::fs::create_dir_all(parent);
+ }
if let Ok(mut file) = File::create(ZELLIJ_STDIN_CACHE_FILE.as_path()) {
let _ = file.write_all(serialized_events.as_bytes());
}
diff --git a/zellij-server/src/lib.rs b/zellij-server/src/lib.rs
index e24aa38e..38628129 100644
--- a/zellij-server/src/lib.rs
+++ b/zellij-server/src/lib.rs
@@ -2012,6 +2012,9 @@ fn should_show_release_notes(
if ZELLIJ_SEEN_RELEASE_NOTES_CACHE_FILE.exists() {
return false;
} else {
+ if let Some(parent) = ZELLIJ_SEEN_RELEASE_NOTES_CACHE_FILE.parent() {
+ let _ = std::fs::create_dir_all(parent);
+ }
if let Err(e) = std::fs::write(&*ZELLIJ_SEEN_RELEASE_NOTES_CACHE_FILE, &[]) {
log::error!(
"Failed to write seen release notes indication to disk: {}",
diff --git a/zellij-server/src/unit/screen_tests.rs b/zellij-server/src/unit/screen_tests.rs
index 059235a1..96dd25aa 100644
--- a/zellij-server/src/unit/screen_tests.rs
+++ b/zellij-server/src/unit/screen_tests.rs
@@ -2196,7 +2196,16 @@ pub fn send_cli_send_keys_action_to_screen() {
send_cli_action_to_server(&session_metadata, cli_action, client_id);
std::thread::sleep(std::time::Duration::from_millis(100));
mock_screen.teardown(vec![pty_writer_thread, screen_thread]);
- assert_snapshot!(format!("{:?}", *received_pty_instructions.lock().unwrap()));
+ let received_write_instructions: Vec<_> = received_pty_instructions
+ .lock()
+ .unwrap()
+ .clone()
+ .into_iter()
+ .filter(|i| matches!(i, PtyWriteInstruction::Write(..)))
+ .collect();
+ // here we assert only the write instructions to make sure they arrived properly and in
+ // sequence to the pane
+ assert_snapshot!(format!("{:#?}", received_write_instructions));
}
#[test]
@@ -2965,7 +2974,15 @@ pub fn send_cli_toggle_active_tab_sync_action() {
send_cli_action_to_server(&session_metadata, cli_write_action, client_id);
std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be
mock_screen.teardown(vec![pty_writer_thread, screen_thread]);
- assert_snapshot!(format!("{:?}", *received_pty_instructions.lock().unwrap()));
+ let received_write_instructions: Vec<_> = received_pty_instructions
+ .lock()
+ .unwrap()
+ .clone()
+ .into_iter()
+ .filter(|i| matches!(i, PtyWriteInstruction::Write(..)))
+ .collect();
+ // here we should have 2 Write instructions, one for each pane
+ assert_snapshot!(format!("{:?}", received_write_instructions));
}
#[test]
diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_send_keys_action_to_screen.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_send_keys_action_to_screen.snap
index 75e4aeef..c2acd715 100644
--- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_send_keys_action_to_screen.snap
+++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_send_keys_action_to_screen.snap
@@ -1,5 +1,20 @@
---
source: zellij-server/src/./unit/screen_tests.rs
-expression: "format!(\"{:?}\", *received_pty_instructions.lock().unwrap())"
+expression: "format!(\"{:#?}\", received_write_instructions)"
---
-[StartCachingResizes, ApplyCachedResizes, StartCachingResizes, ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ApplyCachedResizes, ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ResizePty(0, 119, 18, None, None), ApplyCachedResizes, ApplyCachedResizes, ApplyCachedResizes, StartCachingResizes, ApplyCachedResizes, StartCachingResizes, ApplyCachedResizes, StartCachingResizes, ApplyCachedResizes, StartCachingResizes, Write([1], 0, None), ApplyCachedResizes, StartCachingResizes, ApplyCachedResizes, StartCachingResizes, ApplyCachedResizes, StartCachingResizes, ApplyCachedResizes, StartCachingResizes, Write([120], 0, None), ApplyCachedResizes, StartCachingResizes, ApplyCachedResizes, StartCachingResizes, ApplyCachedResizes, Exit]
+[
+ Write(
+ [
+ 1,
+ ],
+ 0,
+ None,
+ ),
+ Write(
+ [
+ 120,
+ ],
+ 0,
+ None,
+ ),
+]
diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_active_tab_sync_action.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_active_tab_sync_action.snap
index d9fe6929..69157166 100644
--- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_active_tab_sync_action.snap
+++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_toggle_active_tab_sync_action.snap
@@ -1,5 +1,5 @@
---
source: zellij-server/src/./unit/screen_tests.rs
-expression: "format!(\"{:?}\", *received_pty_instructions.lock().unwrap())"
+expression: "format!(\"{:?}\", received_write_instructions)"
---
-[StartCachingResizes, ApplyCachedResizes, StartCachingResizes, ResizePty(0, 59, 18, None, None), ResizePty(1, 58, 18, None, None), ResizePty(0, 59, 18, None, None), ResizePty(1, 58, 18, None, None), ResizePty(0, 59, 18, None, None), ResizePty(1, 58, 18, None, None), ResizePty(0, 59, 18, None, None), ResizePty(1, 58, 18, None, None), ApplyCachedResizes, ResizePty(0, 58, 18, None, None), ResizePty(1, 59, 18, None, None), ResizePty(0, 58, 18, None, None), ResizePty(1, 59, 18, None, None), ResizePty(0, 58, 18, None, None), ResizePty(1, 59, 18, None, None), ResizePty(0, 58, 18, None, None), ResizePty(1, 59, 18, None, None), ResizePty(0, 59, 18, None, None), ResizePty(1, 58, 18, None, None), ApplyCachedResizes, ApplyCachedResizes, ApplyCachedResizes, StartCachingResizes, ApplyCachedResizes, StartCachingResizes, ApplyCachedResizes, StartCachingResizes, ApplyCachedResizes, StartCachingResizes, ApplyCachedResizes, StartCachingResizes, ApplyCachedResizes, StartCachingResizes, Write([102, 111, 111], 0, None), Write([102, 111, 111], 1, None), ApplyCachedResizes, StartCachingResizes, ApplyCachedResizes, StartCachingResizes, ApplyCachedResizes, Exit]
+[Write([102, 111, 111], 0, None), Write([102, 111, 111], 1, None)]
diff --git a/zellij-utils/src/channels.rs b/zellij-utils/src/channels.rs
index 4fdbc8bc..18506329 100644
--- a/zellij-utils/src/channels.rs
+++ b/zellij-utils/src/channels.rs
@@ -5,7 +5,8 @@ use std::cell::RefCell;
use crate::errors::{get_current_ctx, ErrorContext};
pub use crossbeam::channel::{
- bounded, unbounded, Receiver, RecvError, Select, SendError, Sender, TrySendError,
+ bounded, unbounded, Receiver, RecvError, RecvTimeoutError, Select, SendError, Sender,
+ TrySendError,
};
/// An [MPSC](mpsc) asynchronous channel with added error context.