aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2026-03-20 17:28:36 +0100
committerGitHub <noreply@github.com>2026-03-20 17:28:36 +0100
commit4ba41c085ab9207ed31e72a1ba331bff2bebed99 (patch)
treeaf832d3e2b1c4577525f0276bda83fc9a12612b3
parentadjust badges location in readme (diff)
downloadsl0ppij-4ba41c085ab9207ed31e72a1ba331bff2bebed99.tar.gz
sl0ppij-4ba41c085ab9207ed31e72a1ba331bff2bebed99.tar.xz
sl0ppij-4ba41c085ab9207ed31e72a1ba331bff2bebed99.zip
Release notes and fixes (#4879)
* release notes * fix some link issues * rustfmt * fix click wording
-rw-r--r--default-plugins/about/src/active_component.rs17
-rw-r--r--default-plugins/about/src/pages.rs380
-rw-r--r--default-plugins/link/src/main.rs15
-rw-r--r--zellij-server/src/panes/grid.rs29
-rw-r--r--zellij-server/src/plugins/zellij_exports.rs80
-rw-r--r--zellij-tile/src/shim.rs39
-rw-r--r--zellij-utils/assets/prost/api.plugin_command.rs25
-rw-r--r--zellij-utils/src/data.rs7
-rw-r--r--zellij-utils/src/plugin_api/plugin_command.proto13
-rw-r--r--zellij-utils/src/plugin_api/plugin_command.rs68
10 files changed, 537 insertions, 136 deletions
diff --git a/default-plugins/about/src/active_component.rs b/default-plugins/about/src/active_component.rs
index 6e6c716a..7041ca5a 100644
--- a/default-plugins/about/src/active_component.rs
+++ b/default-plugins/about/src/active_component.rs
@@ -55,6 +55,16 @@ impl ActiveComponent {
run_command(&[&executable.borrow(), &link], Default::default());
None
},
+ Some(ClickAction::LaunchPlugin(plugin_url)) => {
+ open_plugin_pane_floating(
+ &plugin_url,
+ Default::default(),
+ None,
+ Default::default(),
+ );
+ self.left_click_action = Some(ClickAction::LaunchPlugin(plugin_url));
+ None
+ },
None => None,
}
}
@@ -125,6 +135,7 @@ impl ComponentCoordinates {
pub enum ClickAction {
ChangePage(Box<dyn FnOnce() -> Page>),
OpenLink(String, Rc<RefCell<String>>), // (destination, executable)
+ LaunchPlugin(String), // plugin URL (e.g. "zellij:share")
}
impl std::fmt::Debug for ClickAction {
@@ -134,6 +145,9 @@ impl std::fmt::Debug for ClickAction {
ClickAction::OpenLink(destination, executable) => {
write!(f, "OpenLink: {}, {:?}", destination, executable)
},
+ ClickAction::LaunchPlugin(url) => {
+ write!(f, "LaunchPlugin: {}", url)
+ },
}
}
}
@@ -148,4 +162,7 @@ impl ClickAction {
pub fn new_open_link(destination: String, executable: Rc<RefCell<String>>) -> Self {
ClickAction::OpenLink(destination, executable)
}
+ pub fn new_launch_plugin(plugin_url: String) -> Self {
+ ClickAction::LaunchPlugin(plugin_url)
+ }
}
diff --git a/default-plugins/about/src/pages.rs b/default-plugins/about/src/pages.rs
index a1107da3..849e7c26 100644
--- a/default-plugins/about/src/pages.rs
+++ b/default-plugins/about/src/pages.rs
@@ -27,51 +27,70 @@ impl Page {
.with_title(main_screen_title(zellij_version.clone(), is_release_notes))
.with_bulletin_list(BulletinList::new(whats_new_title()).with_items(vec![
ActiveComponent::new(TextOrCustomRender::Text(main_menu_item(
- "Web Client",
+ "Windows Support",
)))
.with_hover(TextOrCustomRender::Text(
- main_menu_item("Web Client").selected(),
+ main_menu_item("Windows Support").selected(),
+ ))
+ .with_left_click_action(ClickAction::new_change_page({
+ move || Page::new_windows_support()
+ })),
+ ActiveComponent::new(TextOrCustomRender::Text(main_menu_item(
+ "Remote Sessions",
+ )))
+ .with_hover(TextOrCustomRender::Text(
+ main_menu_item("Remote Sessions").selected(),
))
.with_left_click_action(ClickAction::new_change_page({
let link_executable = link_executable.clone();
- move || Page::new_web_client(link_executable.clone())
+ move || Page::new_remote_sessions(link_executable.clone())
})),
ActiveComponent::new(TextOrCustomRender::Text(main_menu_item(
- "Multiple Pane Select",
+ "Read-Only Session Sharing",
)))
.with_hover(TextOrCustomRender::Text(
- main_menu_item("Multiple Pane Select").selected(),
+ main_menu_item("Read-Only Session Sharing").selected(),
))
- .with_left_click_action(ClickAction::new_change_page(move || {
- Page::new_multiple_select()
+ .with_left_click_action(ClickAction::new_change_page({
+ let link_executable = link_executable.clone();
+ move || Page::new_read_only_sharing(link_executable.clone())
})),
ActiveComponent::new(TextOrCustomRender::Text(main_menu_item(
- "Key Tooltips for the compact-bar",
+ "CLI Automation",
)))
.with_hover(TextOrCustomRender::Text(
- main_menu_item("Key Tooltips for the compact-bar").selected(),
+ main_menu_item("CLI Automation").selected(),
))
.with_left_click_action(ClickAction::new_change_page({
let link_executable = link_executable.clone();
- move || Page::new_key_tooltips_for_compact_bar(link_executable.clone())
+ move || Page::new_cli_automation(link_executable.clone())
})),
ActiveComponent::new(TextOrCustomRender::Text(main_menu_item(
- "Stack Keybinding",
+ "Mouse Resize",
)))
.with_hover(TextOrCustomRender::Text(
- main_menu_item("Stack Keybinding").selected(),
+ main_menu_item("Mouse Resize").selected(),
))
.with_left_click_action(ClickAction::new_change_page(move || {
- Page::new_stack_keybinding()
+ Page::new_mouse_resize()
})),
ActiveComponent::new(TextOrCustomRender::Text(main_menu_item(
- "Performance Improvements",
+ "Click-to-Open File Paths",
)))
.with_hover(TextOrCustomRender::Text(
- main_menu_item("Performance Improvements").selected(),
+ main_menu_item("Click-to-Open File Paths").selected(),
))
.with_left_click_action(ClickAction::new_change_page({
- move || Page::new_performance_improvements()
+ move || Page::new_click_to_open()
+ })),
+ ActiveComponent::new(TextOrCustomRender::Text(main_menu_item(
+ "Layout Manager",
+ )))
+ .with_hover(TextOrCustomRender::Text(
+ main_menu_item("Layout Manager").selected(),
+ ))
+ .with_left_click_action(ClickAction::new_change_page({
+ move || Page::new_layout_manager()
})),
]))
.with_paragraph(vec![ComponentLine::new(vec![
@@ -113,110 +132,183 @@ impl Page {
})
})
}
- pub fn new_web_client(link_executable: Rc<RefCell<String>>) -> Page {
+ fn new_windows_support() -> Page {
Page::new()
- .with_title(Text::new("Web Client").color_range(0, ..))
+ .with_title(Text::new("Windows Support").color_range(0, ..))
.with_paragraph(vec![
- ComponentLine::new(vec![
- // ActiveComponent::new(TextOrCustomRender::Text(Text::new("This version includes a new resizing algorithm that helps better manage panes"))),
- ActiveComponent::new(TextOrCustomRender::Text(Text::new("This version includes a web client, allowing you to share sessions in the browser."))),
- ]),
- ])
- .with_bulletin_list(BulletinList::new(Text::new("The web client:").color_range(2, ..))
- .with_items(vec![
- ActiveComponent::new(TextOrCustomRender::Text(
- Text::new("Allows you to bookmark sessions")
- .color_substring(3, "bookmark sessions")
- )),
- ActiveComponent::new(TextOrCustomRender::Text(
- Text::new("Includes built-in authentication")
- )),
- ActiveComponent::new(TextOrCustomRender::Text(
- Text::new("Can be used as a daily-driver, making your terminal emulator optional")
- )),
- ActiveComponent::new(TextOrCustomRender::Text(
- Text::new("Is completely opt-in")
- )),
- ])
- )
- .with_paragraph(vec![
- ComponentLine::new(vec![
- ActiveComponent::new(TextOrCustomRender::Text(
- Text::new("For more details, see: ")
- .color_range(2, ..)
- )),
- ActiveComponent::new(TextOrCustomRender::Text(Text::new("https://zellij.dev/tutorials/web-client")))
- .with_hover(TextOrCustomRender::CustomRender(Box::new(web_client_screencast_link_selected), Box::new(web_client_screencast_link_selected_len)))
- .with_left_click_action(ClickAction::new_open_link("https://zellij.dev/tutorials/web-client".to_owned(), link_executable.clone()))
- ])
+ ComponentLine::new(vec![ActiveComponent::new(TextOrCustomRender::Text(
+ Text::new("Zellij now runs natively on Windows."),
+ ))]),
+ ComponentLine::new(vec![ActiveComponent::new(TextOrCustomRender::Text(
+ Text::new("Windows users can now enjoy the same workspace management, plugin ecosystem"),
+ ))]),
+ ComponentLine::new(vec![ActiveComponent::new(TextOrCustomRender::Text(
+ Text::new("and multiplayer capabilities that have been available on Linux and macOS."),
+ ))]),
])
- .with_help(Box::new(|hovering_over_link, menu_item_is_selected| esc_go_back_plus_link_hover(hovering_over_link, menu_item_is_selected)))
+ .with_help(Box::new(|_hovering_over_link, _menu_item_is_selected| {
+ esc_to_go_back_help()
+ }))
}
- fn new_multiple_select() -> Page {
+ pub fn new_remote_sessions(link_executable: Rc<RefCell<String>>) -> Page {
Page::new()
- .with_title(Text::new("Multiple Pane Select").color_range(0, ..))
+ .with_title(Text::new("Remote Sessions").color_range(0, ..))
.with_paragraph(vec![
ComponentLine::new(vec![ActiveComponent::new(TextOrCustomRender::Text(
- Text::new("This version adds the ability to perform bulk operations on panes"),
+ Text::new("Attach to remote Zellij sessions over HTTPS, directly from the terminal."),
))]),
ComponentLine::new(vec![ActiveComponent::new(TextOrCustomRender::Text(
- Text::new("eg. close, make floating, break to a new tab, etc."),
+ Text::new("The remote session needs to be running the Zellij web client."),
+ ))]),
+ ComponentLine::new(vec![ActiveComponent::new(TextOrCustomRender::Text(
+ Text::new("Zellij will attach to it exactly as a browser would, through the same interface."),
))]),
])
.with_bulletin_list(
- BulletinList::new(
- Text::new(format!("To select multiple panes: ")).color_range(2, ..),
- )
- .with_items(vec![
- ActiveComponent::new(TextOrCustomRender::Text(
- Text::new(format!("Alt <left-click> them"))
- .color_substring(3, "Alt <left-click>"),
- )),
- ActiveComponent::new(TextOrCustomRender::Text(
- Text::new(format!("Toggle with Alt p")).color_substring(3, "Alt p"),
- )),
- ]),
+ BulletinList::new(Text::new("Try it:").color_range(2, ..))
+ .with_items(vec![
+ ActiveComponent::new(TextOrCustomRender::Text(
+ Text::new("Run the Zellij web server on one machine")
+ .color_substring(3, "Zellij web server"),
+ ))
+ .with_hover(TextOrCustomRender::Text(
+ Text::new("Run the Zellij web server on one machine")
+ .color_substring(3, "Zellij web server")
+ .selected(),
+ ))
+ .with_left_click_action(ClickAction::new_launch_plugin(
+ "zellij:share".to_owned(),
+ )),
+ ActiveComponent::new(TextOrCustomRender::Text(
+ Text::new("From another: zellij attach https://<ip>/<session-name>")
+ .color_substring(3, "zellij attach")
+ .color_substring(2, "https://<ip>/<session-name>"),
+ )),
+ ]),
)
+ .with_paragraph(vec![ComponentLine::new(vec![
+ ActiveComponent::new(TextOrCustomRender::Text(
+ Text::new("Learn more about the web client: ").color_range(2, ..),
+ )),
+ ActiveComponent::new(TextOrCustomRender::Text(Text::new(
+ "https://zellij.dev/tutorials/web-client/",
+ )))
+ .with_hover(TextOrCustomRender::CustomRender(
+ Box::new(web_client_link_selected),
+ Box::new(web_client_link_selected_len),
+ ))
+ .with_left_click_action(ClickAction::new_open_link(
+ "https://zellij.dev/tutorials/web-client/".to_owned(),
+ link_executable.clone(),
+ )),
+ ])])
+ .with_help(Box::new(|hovering_over_link, menu_item_is_selected| {
+ esc_go_back_plus_link_hover(hovering_over_link, menu_item_is_selected)
+ }))
+ }
+ fn new_read_only_sharing(link_executable: Rc<RefCell<String>>) -> Page {
+ Page::new()
+ .with_title(Text::new("Read-Only Session Sharing").color_range(0, ..))
.with_paragraph(vec![
ComponentLine::new(vec![ActiveComponent::new(TextOrCustomRender::Text(
- Text::new("To disable this behavior (and the associated hover effects)"),
+ Text::new("Sessions can now be shared in read-only mode."),
+ ))]),
+ ComponentLine::new(vec![ActiveComponent::new(TextOrCustomRender::Text(
+ Text::new(
+ "Useful for demonstrations, teaching, monitoring and pair programming",
+ )
+ .color_substring(2, "demonstrations")
+ .color_substring(2, "teaching")
+ .color_substring(2, "monitoring")
+ .color_substring(2, "pair programming"),
))]),
ComponentLine::new(vec![ActiveComponent::new(TextOrCustomRender::Text(
- Text::new(format!("add advanced_mouse_actions false to the config."))
- .color_substring(3, "advanced_mouse_actions false"),
+ Text::new("where one participant should observe without interfering."),
))]),
])
- .with_help(Box::new(|_hovering_over_link, _menu_item_is_selected| {
- esc_to_go_back_help()
+ .with_paragraph(vec![
+ ComponentLine::new(vec![ActiveComponent::new(TextOrCustomRender::Text(
+ Text::new("Create a read-only web token with:")
+ .color_substring(2, "read-only web token"),
+ ))]),
+ ComponentLine::new(vec![ActiveComponent::new(TextOrCustomRender::Text(
+ Text::new("zellij web --create-read-only-token").color_range(3, ..),
+ ))]),
+ ])
+ .with_paragraph(vec![ComponentLine::new(vec![ActiveComponent::new(
+ TextOrCustomRender::Text(Text::new(
+ "Share the token for view-only access without risk of unintended input.",
+ )),
+ )])])
+ .with_paragraph(vec![ComponentLine::new(vec![
+ ActiveComponent::new(TextOrCustomRender::Text(
+ Text::new("Learn more: ").color_range(2, ..),
+ )),
+ ActiveComponent::new(TextOrCustomRender::Text(Text::new(
+ "https://zellij.dev/tutorials/web-client/",
+ )))
+ .with_hover(TextOrCustomRender::CustomRender(
+ Box::new(web_client_link_selected),
+ Box::new(web_client_link_selected_len),
+ ))
+ .with_left_click_action(ClickAction::new_open_link(
+ "https://zellij.dev/tutorials/web-client/".to_owned(),
+ link_executable.clone(),
+ )),
+ ])])
+ .with_help(Box::new(|hovering_over_link, menu_item_is_selected| {
+ esc_go_back_plus_link_hover(hovering_over_link, menu_item_is_selected)
}))
}
- fn new_key_tooltips_for_compact_bar(link_executable: Rc<RefCell<String>>) -> Page {
+ fn new_cli_automation(link_executable: Rc<RefCell<String>>) -> Page {
Page::new()
- .with_title(Text::new("Key Tooltips for the compact-bar").color_range(0, ..))
+ .with_title(Text::new("CLI Automation").color_range(0, ..))
.with_paragraph(vec![
ComponentLine::new(vec![ActiveComponent::new(TextOrCustomRender::Text(
- Text::new(
- "Starting this version, it's possible to add toggle-able key tooltips",
- )
- .color_range(3, 37..=58),
+ Text::new("This release significantly expands the CLI's control surface,"),
))]),
ComponentLine::new(vec![ActiveComponent::new(TextOrCustomRender::Text(
- Text::new("when using the compact-bar.").color_substring(3, "compact-bar"),
+ Text::new("enabling the building of powerful workspace automations."),
))]),
])
+ .with_bulletin_list(
+ BulletinList::new(Text::new("New and expanded capabilities:").color_range(2, ..))
+ .with_items(vec![
+ ActiveComponent::new(TextOrCustomRender::Text(
+ Text::new("list-panes, list-tabs, dump-screen, dump-layout with --json output")
+ .color_substring(3, "list-panes")
+ .color_substring(3, "list-tabs")
+ .color_substring(3, "dump-screen")
+ .color_substring(3, "dump-layout")
+ .color_substring(3, "--json"),
+ )),
+ ActiveComponent::new(TextOrCustomRender::Text(
+ Text::new("zellij run optionally blocks until success/failure")
+ .color_substring(3, "zellij run"),
+ )),
+ ActiveComponent::new(TextOrCustomRender::Text(
+ Text::new("zellij subscribe can stream pane scrollback in real time")
+ .color_substring(3, "zellij subscribe"),
+ )),
+ ActiveComponent::new(TextOrCustomRender::Text(
+ Text::new("zellij send-keys/paste can send human readable keys to other panes or sessions")
+ .color_substring(3, "zellij send-keys/paste"),
+ )),
+ ]),
+ )
.with_paragraph(vec![ComponentLine::new(vec![
ActiveComponent::new(TextOrCustomRender::Text(
- Text::new("For more information: ").color_range(2, ..),
+ Text::new("Learn more: ").color_range(2, ..),
)),
ActiveComponent::new(TextOrCustomRender::Text(Text::new(
- "https://zellij.dev/documentation/faq.html",
+ "https://zellij.dev/documentation/controlling-zellij-through-cli.html",
)))
.with_hover(TextOrCustomRender::CustomRender(
- Box::new(compact_bar_link_selected),
- Box::new(compact_bar_link_selected_len),
+ Box::new(cli_automation_link_selected),
+ Box::new(cli_automation_link_selected_len),
))
.with_left_click_action(ClickAction::new_open_link(
- "https://zellij.dev/documentation/faq.html".to_owned(),
+ "https://zellij.dev/documentation/controlling-zellij-through-cli.html".to_owned(),
link_executable.clone(),
)),
])])
@@ -224,44 +316,107 @@ impl Page {
esc_go_back_plus_link_hover(hovering_over_link, menu_item_is_selected)
}))
}
- fn new_stack_keybinding() -> Page {
+ fn new_mouse_resize() -> Page {
Page::new()
- .with_title(Text::new("New Stack Keybinding").color_range(0, ..))
+ .with_title(Text::new("Mouse Resize").color_range(0, ..))
.with_paragraph(vec![
ComponentLine::new(vec![ActiveComponent::new(TextOrCustomRender::Text(
- Text::new("It's now possible to open a stacked pane directly on top of the current pane").color_substring(2, "stacked pane"),
+ Text::new("Panes can now be resized by dragging their borders with the mouse."),
+ ))]),
+ ComponentLine::new(vec![ActiveComponent::new(TextOrCustomRender::Text(
+ Text::new("Tiled panes can be resized with or without Ctrl held down.")
+ .color_substring(3, "Ctrl"),
+ ))]),
+ ComponentLine::new(vec![ActiveComponent::new(TextOrCustomRender::Text(
+ Text::new("Floating panes require Ctrl+drag to resize.")
+ .color_substring(3, "Ctrl+drag"),
))]),
])
+ .with_paragraph(vec![ComponentLine::new(vec![ActiveComponent::new(
+ TextOrCustomRender::Text(
+ Text::new("Try it: Ctrl+drag on the borders of this pane.")
+ .color_substring(2, "Try it:")
+ .color_substring(3, "Ctrl+drag"),
+ ),
+ )])])
+ .with_help(Box::new(|_hovering_over_link, _menu_item_is_selected| {
+ esc_to_go_back_help()
+ }))
+ }
+ fn new_click_to_open() -> Page {
+ Page::new()
+ .with_title(Text::new("Click-to-Open File Paths").color_range(0, ..))
.with_paragraph(vec![
- ComponentLine::new(vec![
- ActiveComponent::new(TextOrCustomRender::Text(
- Text::new("By default: Ctrl p + s").color_substring(3, "Ctrl p").color_substring(3, " s"),
- )),
- ]),
- ComponentLine::new(vec![
- ActiveComponent::new(TextOrCustomRender::Text(
- Text::new("In unlock first: Ctrl g + p + s").color_substring(3, "Ctrl g").color_substring(3, " p").color_substring(3, " s"),
- )),
- ]),
+ ComponentLine::new(vec![ActiveComponent::new(TextOrCustomRender::Text(
+ Text::new("Zellij now detects file paths in the terminal viewport."),
+ ))]),
+ ComponentLine::new(vec![ActiveComponent::new(TextOrCustomRender::Text(
+ Text::new("Alt-Click on a file path to open it.")
+ .color_substring(3, "Alt-Click"),
+ ))]),
+ ])
+ .with_paragraph(vec![
+ ComponentLine::new(vec![ActiveComponent::new(TextOrCustomRender::Text(
+ Text::new("Useful for navigating compiler errors, grep results,")
+ .color_substring(2, "compiler errors")
+ .color_substring(2, "grep results"),
+ ))]),
+ ComponentLine::new(vec![ActiveComponent::new(TextOrCustomRender::Text(
+ Text::new("log files, or any output containing file paths.")
+ .color_substring(2, "log files"),
+ ))]),
])
.with_paragraph(vec![
ComponentLine::new(vec![ActiveComponent::new(TextOrCustomRender::Text(
- Text::new("To add to an existing config, see the release notes.")
+ Text::new("Plugins can also highlight arbitrary text in the viewport,"),
+ ))]),
+ ComponentLine::new(vec![ActiveComponent::new(TextOrCustomRender::Text(
+ Text::new("opening possibilities for custom link handlers")
+ .color_substring(3, "custom link handlers"),
+ ))]),
+ ComponentLine::new(vec![ActiveComponent::new(TextOrCustomRender::Text(
+ Text::new("and interactive overlays.")
+ .color_substring(3, "interactive overlays"),
))]),
])
.with_help(Box::new(|_hovering_over_link, _menu_item_is_selected| {
esc_to_go_back_help()
}))
}
- fn new_performance_improvements() -> Page {
+ fn new_layout_manager() -> Page {
Page::new()
- .with_title(Text::new("Performance Improvements").color_range(0, ..))
+ .with_title(Text::new("Layout Manager").color_range(0, ..))
+ .with_paragraph(vec![
+ ComponentLine::new(vec![
+ ActiveComponent::new(TextOrCustomRender::Text(Text::new("A new "))),
+ ActiveComponent::new(TextOrCustomRender::Text(
+ Text::new("layout-manager interface").color_range(3, ..),
+ ))
+ .with_hover(TextOrCustomRender::Text(
+ Text::new("layout-manager interface")
+ .color_range(3, ..)
+ .selected(),
+ ))
+ .with_left_click_action(ClickAction::new_launch_plugin(
+ "zellij:layout-manager".to_owned(),
+ )),
+ ActiveComponent::new(TextOrCustomRender::Text(Text::new(
+ " allows overriding layouts at runtime.",
+ ))),
+ ]),
+ ComponentLine::new(vec![ActiveComponent::new(TextOrCustomRender::Text(
+ Text::new(
+ "Workspaces can be reconfigured dynamically without restarting sessions.",
+ ),
+ ))]),
+ ])
.with_paragraph(vec![
ComponentLine::new(vec![ActiveComponent::new(TextOrCustomRender::Text(
- Text::new("This version adds a debounced asynchronous render mechanism"),
+ Text::new("Access it through the session menu, or run:")
+ .color_substring(2, "session menu"),
))]),
ComponentLine::new(vec![ActiveComponent::new(TextOrCustomRender::Text(
- Text::new("making rendering much smoother across the whole application."),
+ Text::new("zellij plugin -- zellij:layout-manager").color_range(3, ..),
))]),
])
.with_help(Box::new(|_hovering_over_link, _menu_item_is_selected| {
@@ -597,29 +752,30 @@ fn sponsors_link_text_selected_len() -> usize {
34
}
-fn web_client_screencast_link_selected(x: usize, y: usize) -> usize {
+fn cli_automation_link_selected(x: usize, y: usize) -> usize {
print!(
- "\u{1b}[{};{}H\u{1b}[m\u{1b}[1;4mhttps://zellij.dev/tutorials/web-client",
+ "\u{1b}[{};{}H\u{1b}[m\u{1b}[1;4mhttps://zellij.dev/documentation/controlling-zellij-through-cli.html",
y + 1,
x + 1
);
- 39
+ 68
}
-fn web_client_screencast_link_selected_len() -> usize {
- 39
+fn cli_automation_link_selected_len() -> usize {
+ 68
}
-fn compact_bar_link_selected(x: usize, y: usize) -> usize {
+fn web_client_link_selected(x: usize, y: usize) -> usize {
print!(
- "\u{1b}[{};{}H\u{1b}[m\u{1b}[1;4mhttps://zellij.dev/documentation/faq.html",
+ "\u{1b}[{};{}H\u{1b}[m\u{1b}[1;4mhttps://zellij.dev/tutorials/web-client/",
y + 1,
x + 1
);
- 41
+ 40
}
-fn compact_bar_link_selected_len() -> usize {
- 41
+
+fn web_client_link_selected_len() -> usize {
+ 40
}
// Text components
diff --git a/default-plugins/link/src/main.rs b/default-plugins/link/src/main.rs
index f2127d44..f27076b5 100644
--- a/default-plugins/link/src/main.rs
+++ b/default-plugins/link/src/main.rs
@@ -2,7 +2,7 @@ use std::collections::{BTreeMap, HashMap, HashSet};
use std::path::{Path, PathBuf};
use zellij_tile::prelude::*;
-const FILE_PATH_REGEX: &str = r#"(?:(?:\./|\.\./|/)[A-Za-z0-9_./\-+@%,#=~!\$\{\}\[\]]+|~/[A-Za-z0-9_./\-+@%,#=~!\$\{\}\[\]]+|\$\{?[A-Za-z_][A-Za-z0-9_]*\}?/[A-Za-z0-9_./\-+@%,#=~!\$\{\}\[\]]+|[a-zA-Z0-9_][a-zA-Z0-9_.\-]*/[A-Za-z0-9_./\-+@%,#=~!\$\{\}\[\]]+)(?::\d+(?::\d+)?)?"#;
+const FILE_PATH_REGEX: &str = r#"(?:^|\s)((?:(?:\./|\.\./|/)[A-Za-z0-9_./\-+@%,#=~!\$\{\}\[\]]+|~/[A-Za-z0-9_./\-+@%,#=~!\$\{\}\[\]]+|\$\{?[A-Za-z_][A-Za-z0-9_]*\}?/[A-Za-z0-9_./\-+@%,#=~!\$\{\}\[\]]+)(?::\d+(?::\d+)?)?)(?:\s|$)"#;
const CWD_CONTEXT_KEY: &str = "cwd";
@@ -187,7 +187,11 @@ impl State {
// Directory-entry patterns for the pane's current CWD
if let Some(entries) = self.pane_dir_entries.get(&pane_id) {
for entry_name in entries {
- let pattern = regex_escape(entry_name);
+ let path_chars = r#"[A-Za-z0-9_./\-+@%,#=~!\$\{\}\[\]]"#;
+ let pattern = format!(
+ "(?:^|\\s)({}(?:/{path_chars}+)?(?::\\d+(?::\\d+)?)?)(?:\\s|$)",
+ regex_escape(entry_name),
+ );
highlights.push(RegexHighlight {
pattern,
style: HighlightStyle::None,
@@ -222,6 +226,10 @@ impl State {
/// Scan a directory for first-level file and folder names.
/// Returns an empty vec on any error.
+/// Maximum number of directory entries to scan. Directories with more entries
+/// than this are skipped entirely to avoid excessive regex pattern count.
+const MAX_DIR_ENTRIES: usize = 500;
+
fn scan_directory(path: &Path) -> Vec<String> {
let mut entries = Vec::new();
let read_dir = match std::fs::read_dir(path) {
@@ -232,6 +240,9 @@ fn scan_directory(path: &Path) -> Vec<String> {
if let Ok(entry) = entry {
if let Some(name) = entry.file_name().to_str() {
entries.push(name.to_owned());
+ if entries.len() > MAX_DIR_ENTRIES {
+ return Vec::new();
+ }
}
}
}
diff --git a/zellij-server/src/panes/grid.rs b/zellij-server/src/panes/grid.rs
index 0f0e3fb4..07752542 100644
--- a/zellij-server/src/panes/grid.rs
+++ b/zellij-server/src/panes/grid.rs
@@ -458,6 +458,15 @@ fn match_to_selection(
Some((sel, start_row, start_col, end_row, end_col))
}
+/// Extract the effective match from a set of captures.
+/// If capture group 1 exists, it is used (allowing patterns to include
+/// context such as surrounding whitespace in the full match while
+/// highlighting only the content in group 1). Otherwise the full match
+/// (group 0) is returned.
+fn highlight_match<'t>(captures: &regex::Captures<'t>) -> Option<regex::Match<'t>> {
+ captures.get(1).or_else(|| captures.get(0))
+}
+
/// Check whether a (row, col) position falls within a display span.
/// The span is inclusive at start and exclusive at end.
fn position_in_span(
@@ -2163,7 +2172,10 @@ impl Grid {
)> = None;
for (plugin_id, pattern_map) in &self.plugin_highlights {
for (pattern, compiled) in pattern_map {
- for mat in compiled.regex.find_iter(&logical_text) {
+ for captures in compiled.regex.captures_iter(&logical_text) {
+ let Some(mat) = highlight_match(&captures) else {
+ continue;
+ };
if let Some((_sel, start_row, start_col, end_row, end_col)) =
match_to_selection(&mat, &boundaries, &self.viewport)
{
@@ -2250,7 +2262,10 @@ impl Grid {
if !compiled.on_hover || compiled.tooltip_text.is_none() {
continue;
}
- for mat in compiled.regex.find_iter(&logical_text) {
+ for captures in compiled.regex.captures_iter(&logical_text) {
+ let Some(mat) = highlight_match(&captures) else {
+ continue;
+ };
if let Some((_sel, start_row, start_col, end_row, end_col)) =
match_to_selection(&mat, &boundaries, &self.viewport)
{
@@ -2306,7 +2321,10 @@ impl Grid {
if !compiled.on_hover || !compiled.has_visual_effect() {
continue;
}
- for mat in compiled.regex.find_iter(&logical_text) {
+ for captures in compiled.regex.captures_iter(&logical_text) {
+ let Some(mat) = highlight_match(&captures) else {
+ continue;
+ };
if let Some((sel, start_row, start_col, end_row, end_col)) =
match_to_selection(&mat, &boundaries, &self.viewport)
{
@@ -2340,7 +2358,10 @@ impl Grid {
if compiled.on_hover || !compiled.has_visual_effect() {
continue;
}
- for mat in compiled.regex.find_iter(&logical_text) {
+ for captures in compiled.regex.captures_iter(&logical_text) {
+ let Some(mat) = highlight_match(&captures) else {
+ continue;
+ };
if let Some((sel, _, _, _, _)) =
match_to_selection(&mat, &boundaries, &self.viewport)
{
diff --git a/zellij-server/src/plugins/zellij_exports.rs b/zellij-server/src/plugins/zellij_exports.rs
index c96f028d..9b6f022f 100644
--- a/zellij-server/src/plugins/zellij_exports.rs
+++ b/zellij-server/src/plugins/zellij_exports.rs
@@ -33,8 +33,8 @@ use zellij_utils::data::{
OpenEditPaneInPlaceOfPaneIdResponse, OpenFileFloatingNearPluginResponse,
OpenFileFloatingResponse, OpenFileInPlaceOfPluginResponse, OpenFileInPlaceResponse,
OpenFileNearPluginResponse, OpenFileResponse, OpenPaneInNewTabResponse,
- OpenTerminalFloatingNearPluginResponse, OpenTerminalFloatingResponse,
- OpenTerminalInPlaceOfPluginResponse, OpenTerminalInPlaceResponse,
+ OpenPluginPaneFloatingResponse, OpenTerminalFloatingNearPluginResponse,
+ OpenTerminalFloatingResponse, OpenTerminalInPlaceOfPluginResponse, OpenTerminalInPlaceResponse,
OpenTerminalNearPluginResponse, OpenTerminalPaneInPlaceOfPaneIdResponse, OpenTerminalResponse,
OriginatingPlugin, PaneScrollbackResponse, PermissionStatus, PermissionType, PluginPermission,
RegexHighlight, RenameLayoutResponse, SaveLayoutResponse, TabMetadata,
@@ -94,9 +94,10 @@ use zellij_utils::{
ProtobufOpenFileFloatingNearPluginResponse, ProtobufOpenFileFloatingResponse,
ProtobufOpenFileInPlaceOfPluginResponse, ProtobufOpenFileInPlaceResponse,
ProtobufOpenFileNearPluginResponse, ProtobufOpenFileResponse,
- ProtobufOpenPaneInNewTabResponse, ProtobufOpenTerminalFloatingNearPluginResponse,
- ProtobufOpenTerminalFloatingResponse, ProtobufOpenTerminalInPlaceOfPluginResponse,
- ProtobufOpenTerminalInPlaceResponse, ProtobufOpenTerminalNearPluginResponse,
+ ProtobufOpenPaneInNewTabResponse, ProtobufOpenPluginPaneFloatingResponse,
+ ProtobufOpenTerminalFloatingNearPluginResponse, ProtobufOpenTerminalFloatingResponse,
+ ProtobufOpenTerminalInPlaceOfPluginResponse, ProtobufOpenTerminalInPlaceResponse,
+ ProtobufOpenTerminalNearPluginResponse,
ProtobufOpenTerminalPaneInPlaceOfPaneIdResponse, ProtobufOpenTerminalResponse,
ProtobufParseLayoutResponse, ProtobufPluginCommand, ProtobufRenameLayoutResponse,
ProtobufSaveLayoutResponse, ProtobufSaveSessionResponse,
@@ -700,6 +701,20 @@ fn host_run_plugin_command(mut caller: Caller<'_, PluginEnv>) {
} => {
open_plugin_pane_in_new_tab(env, plugin_url, configuration, context);
},
+ PluginCommand::OpenPluginPaneFloating {
+ plugin_url,
+ configuration,
+ floating_pane_coordinates,
+ context,
+ } => {
+ open_plugin_pane_floating(
+ env,
+ plugin_url,
+ configuration,
+ floating_pane_coordinates,
+ context,
+ );
+ },
PluginCommand::OpenEditorPaneInNewTab(file_to_open, context) => {
open_editor_pane_in_new_tab(env, file_to_open, context);
},
@@ -1113,6 +1128,53 @@ fn open_plugin_pane_in_new_tab(
.non_fatal();
}
+fn open_plugin_pane_floating(
+ env: &PluginEnv,
+ plugin_url: String,
+ configuration: BTreeMap<String, String>,
+ floating_pane_coordinates: Option<FloatingPaneCoordinates>,
+ context: BTreeMap<String, String>,
+) {
+ let run_plugin_or_alias = RunPluginOrAlias::from_url(
+ &plugin_url,
+ &Some(configuration),
+ None,
+ Some(env.plugin_cwd.clone()),
+ );
+ let run_plugin_or_alias = match run_plugin_or_alias {
+ Ok(r) => r,
+ Err(e) => {
+ log::error!("Failed to parse plugin url '{}': {}", plugin_url, e);
+ let response = ProtobufOpenPluginPaneFloatingResponse::from(
+ OpenPluginPaneFloatingResponse::default(),
+ );
+ wasi_write_object(env, &response.encode_to_vec())
+ .with_context(|| {
+ format!("failed to write open_plugin_pane_floating error response")
+ })
+ .non_fatal();
+ return;
+ },
+ };
+ let _ = context; // context is not currently used for plugin panes
+ let action = Action::NewFloatingPluginPane {
+ plugin: run_plugin_or_alias,
+ pane_name: None,
+ skip_cache: false,
+ cwd: Some(env.plugin_cwd.clone()),
+ coordinates: floating_pane_coordinates,
+ };
+ let error_msg = || format!("Failed to open floating plugin pane");
+ let result = apply_action!(action, error_msg, env);
+
+ let pane_id: OpenPluginPaneFloatingResponse =
+ result.and_then(|r| r.affected_pane_id).map(|p| p.into());
+ let response = ProtobufOpenPluginPaneFloatingResponse::from(pane_id);
+ wasi_write_object(env, &response.encode_to_vec())
+ .with_context(|| format!("failed to write open_plugin_pane_floating response"))
+ .non_fatal();
+}
+
fn open_editor_pane_in_new_tab(
env: &PluginEnv,
file_to_open: FileToOpen,
@@ -5014,6 +5076,8 @@ fn check_command_permission(
| PluginCommand::OpenTerminalFloatingNearPlugin(..)
| PluginCommand::OpenTerminalInPlace(..)
| PluginCommand::OpenTerminalInPlaceOfPlugin(..)
+ | PluginCommand::OpenPluginPaneInNewTab { .. }
+ | PluginCommand::OpenPluginPaneFloating { .. }
| PluginCommand::OpenTerminalPaneInPlaceOfPaneId(..) => {
PermissionType::OpenTerminalsOrPlugins
},
@@ -5173,9 +5237,9 @@ fn check_command_permission(
PluginCommand::GetSessionEnvironmentVariables => {
PermissionType::ReadSessionEnvironmentVariables
},
- PluginCommand::OpenCommandPaneInNewTab(..)
- | PluginCommand::OpenPluginPaneInNewTab { .. }
- | PluginCommand::OpenEditorPaneInNewTab(..) => PermissionType::ChangeApplicationState,
+ PluginCommand::OpenCommandPaneInNewTab(..) | PluginCommand::OpenEditorPaneInNewTab(..) => {
+ PermissionType::ChangeApplicationState
+ },
_ => return (PermissionStatus::Granted, None),
};
diff --git a/zellij-tile/src/shim.rs b/zellij-tile/src/shim.rs
index 167df3ec..db07548f 100644
--- a/zellij-tile/src/shim.rs
+++ b/zellij-tile/src/shim.rs
@@ -31,13 +31,14 @@ use zellij_utils::plugin_api::plugin_command::{
ProtobufOpenEditPaneInPlaceOfPaneIdResponse, ProtobufOpenFileFloatingNearPluginResponse,
ProtobufOpenFileFloatingResponse, ProtobufOpenFileInPlaceOfPluginResponse,
ProtobufOpenFileInPlaceResponse, ProtobufOpenFileNearPluginResponse, ProtobufOpenFileResponse,
- ProtobufOpenPaneInNewTabResponse, ProtobufOpenTerminalFloatingNearPluginResponse,
- ProtobufOpenTerminalFloatingResponse, ProtobufOpenTerminalInPlaceOfPluginResponse,
- ProtobufOpenTerminalInPlaceResponse, ProtobufOpenTerminalNearPluginResponse,
- ProtobufOpenTerminalPaneInPlaceOfPaneIdResponse, ProtobufOpenTerminalResponse,
- ProtobufParseLayoutResponse, ProtobufPluginCommand, ProtobufRenameLayoutResponse,
- ProtobufSaveLayoutResponse, ProtobufSaveSessionResponse, ProtobufShowFloatingPanesResponse,
- RenameWebTokenResponse, RevokeAllWebTokensResponse, RevokeTokenResponse,
+ ProtobufOpenPaneInNewTabResponse, ProtobufOpenPluginPaneFloatingResponse,
+ ProtobufOpenTerminalFloatingNearPluginResponse, ProtobufOpenTerminalFloatingResponse,
+ ProtobufOpenTerminalInPlaceOfPluginResponse, ProtobufOpenTerminalInPlaceResponse,
+ ProtobufOpenTerminalNearPluginResponse, ProtobufOpenTerminalPaneInPlaceOfPaneIdResponse,
+ ProtobufOpenTerminalResponse, ProtobufParseLayoutResponse, ProtobufPluginCommand,
+ ProtobufRenameLayoutResponse, ProtobufSaveLayoutResponse, ProtobufSaveSessionResponse,
+ ProtobufShowFloatingPanesResponse, RenameWebTokenResponse, RevokeAllWebTokensResponse,
+ RevokeTokenResponse,
};
use zellij_utils::plugin_api::plugin_ids::{ProtobufPluginIds, ProtobufZellijVersion};
@@ -998,6 +999,30 @@ pub fn open_plugin_pane_in_new_tab(
(result.tab_id, result.pane_id)
}
+/// Open a new floating plugin pane with the specified plugin URL and configuration.
+/// Returns the pane ID of the newly created plugin pane, if successful.
+pub fn open_plugin_pane_floating(
+ plugin_url: &str,
+ configuration: BTreeMap<String, String>,
+ coordinates: Option<FloatingPaneCoordinates>,
+ context: BTreeMap<String, String>,
+) -> Option<PaneId> {
+ let plugin_command = PluginCommand::OpenPluginPaneFloating {
+ plugin_url: plugin_url.to_owned(),
+ configuration,
+ floating_pane_coordinates: coordinates,
+ context,
+ };
+ let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap();
+ object_to_stdout(&protobuf_plugin_command.encode_to_vec());
+ unsafe { host_run_plugin_command() };
+
+ let response =
+ ProtobufOpenPluginPaneFloatingResponse::decode(bytes_from_stdin().unwrap().as_slice())
+ .unwrap();
+ OpenPluginPaneFloatingResponse::try_from(response).unwrap()
+}
+
/// Opens a new tab with an editor pane for `file_to_open`.
/// Returns `(tab_id, pane_id)` of the created tab and pane.
pub fn open_editor_pane_in_new_tab(
diff --git a/zellij-utils/assets/prost/api.plugin_command.rs b/zellij-utils/assets/prost/api.plugin_command.rs
index ccb77f81..52626ac6 100644
--- a/zellij-utils/assets/prost/api.plugin_command.rs
+++ b/zellij-utils/assets/prost/api.plugin_command.rs
@@ -3,7 +3,7 @@
pub struct PluginCommand {
#[prost(enumeration="CommandName", tag="1")]
pub name: i32,
- #[prost(oneof="plugin_command::Payload", tags="2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159")]
+ #[prost(oneof="plugin_command::Payload", tags="2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160")]
pub payload: ::core::option::Option<plugin_command::Payload>,
}
/// Nested message and enum types in `PluginCommand`.
@@ -301,6 +301,8 @@ pub mod plugin_command {
SetPaneRegexHighlightsPayload(super::SetPaneRegexHighlightsPayload),
#[prost(message, tag="159")]
ClearPaneHighlightsPayload(super::ClearPaneHighlightsPayload),
+ #[prost(message, tag="160")]
+ OpenPluginPaneFloatingPayload(super::OpenPluginPaneFloatingPayload),
}
}
#[allow(clippy::derive_partial_eq_without_eq)]
@@ -1879,6 +1881,24 @@ pub struct ClearPaneHighlightsPayload {
#[prost(message, optional, tag="1")]
pub pane_id: ::core::option::Option<PaneId>,
}
+#[allow(clippy::derive_partial_eq_without_eq)]
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct OpenPluginPaneFloatingPayload {
+ #[prost(string, tag="1")]
+ pub plugin_url: ::prost::alloc::string::String,
+ #[prost(map="string, string", tag="2")]
+ pub configuration: ::std::collections::HashMap<::prost::alloc::string::String, ::prost::alloc::string::String>,
+ #[prost(message, optional, tag="3")]
+ pub floating_pane_coordinates: ::core::option::Option<FloatingPaneCoordinates>,
+ #[prost(message, repeated, tag="4")]
+ pub context: ::prost::alloc::vec::Vec<ContextItem>,
+}
+#[allow(clippy::derive_partial_eq_without_eq)]
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct OpenPluginPaneFloatingResponse {
+ #[prost(message, optional, tag="1")]
+ pub pane_id: ::core::option::Option<PaneId>,
+}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
#[repr(i32)]
pub enum CommandName {
@@ -2070,6 +2090,7 @@ pub enum CommandName {
SetPaneColor = 206,
SetPaneRegexHighlights = 207,
ClearPaneHighlights = 208,
+ OpenPluginPaneFloating = 209,
}
impl CommandName {
/// String value of the enum field names used in the ProtoBuf definition.
@@ -2266,6 +2287,7 @@ impl CommandName {
CommandName::SetPaneColor => "SetPaneColor",
CommandName::SetPaneRegexHighlights => "SetPaneRegexHighlights",
CommandName::ClearPaneHighlights => "ClearPaneHighlights",
+ CommandName::OpenPluginPaneFloating => "OpenPluginPaneFloating",
}
}
/// Creates an enum from field names used in the ProtoBuf definition.
@@ -2459,6 +2481,7 @@ impl CommandName {
"SetPaneColor" => Some(Self::SetPaneColor),
"SetPaneRegexHighlights" => Some(Self::SetPaneRegexHighlights),
"ClearPaneHighlights" => Some(Self::ClearPaneHighlights),
+ "OpenPluginPaneFloating" => Some(Self::OpenPluginPaneFloating),
_ => None,
}
}
diff --git a/zellij-utils/src/data.rs b/zellij-utils/src/data.rs
index 46a0daa5..1fdea34e 100644
--- a/zellij-utils/src/data.rs
+++ b/zellij-utils/src/data.rs
@@ -3555,6 +3555,12 @@ pub enum PluginCommand {
SetPaneColor(PaneId, Option<String>, Option<String>), // (pane_id, fg, bg)
SetPaneRegexHighlights(PaneId, Vec<RegexHighlight>),
ClearPaneHighlights(PaneId),
+ OpenPluginPaneFloating {
+ plugin_url: String,
+ configuration: BTreeMap<String, String>,
+ floating_pane_coordinates: Option<FloatingPaneCoordinates>,
+ context: BTreeMap<String, String>,
+ },
}
// Response type for plugin API methods that open a pane in a new tab
@@ -3597,3 +3603,4 @@ pub type OpenCommandPaneBackgroundResponse = Option<PaneId>;
pub type OpenCommandPaneInPlaceOfPaneIdResponse = Option<PaneId>;
pub type OpenTerminalPaneInPlaceOfPaneIdResponse = Option<PaneId>;
pub type OpenEditPaneInPlaceOfPaneIdResponse = Option<PaneId>;
+pub type OpenPluginPaneFloatingResponse = Option<PaneId>;
diff --git a/zellij-utils/src/plugin_api/plugin_command.proto b/zellij-utils/src/plugin_api/plugin_command.proto
index 0634d63e..f00ffdd1 100644
--- a/zellij-utils/src/plugin_api/plugin_command.proto
+++ b/zellij-utils/src/plugin_api/plugin_command.proto
@@ -201,6 +201,7 @@ enum CommandName {
SetPaneColor = 206;
SetPaneRegexHighlights = 207;
ClearPaneHighlights = 208;
+ OpenPluginPaneFloating = 209;
}
message PluginCommand {
@@ -351,6 +352,7 @@ message PluginCommand {
SetPaneColorPayload set_pane_color_payload = 157;
SetPaneRegexHighlightsPayload set_pane_regex_highlights_payload = 158;
ClearPaneHighlightsPayload clear_pane_highlights_payload = 159;
+ OpenPluginPaneFloatingPayload open_plugin_pane_floating_payload = 160;
}
}
@@ -1281,3 +1283,14 @@ message SetPaneRegexHighlightsPayload {
message ClearPaneHighlightsPayload {
PaneId pane_id = 1;
}
+
+message OpenPluginPaneFloatingPayload {
+ string plugin_url = 1;
+ map<string, string> configuration = 2;
+ optional FloatingPaneCoordinates floating_pane_coordinates = 3;
+ repeated ContextItem context = 4;
+}
+
+message OpenPluginPaneFloatingResponse {
+ optional PaneId pane_id = 1;
+}
diff --git a/zellij-utils/src/plugin_api/plugin_command.rs b/zellij-utils/src/plugin_api/plugin_command.rs
index 00acb760..fc55dd91 100644
--- a/zellij-utils/src/plugin_api/plugin_command.rs
+++ b/zellij-utils/src/plugin_api/plugin_command.rs
@@ -81,6 +81,8 @@ pub use super::generated_api::api::{
OpenFileNearPluginResponse as ProtobufOpenFileNearPluginResponse, OpenFilePayload,
OpenFileResponse as ProtobufOpenFileResponse,
OpenPaneInNewTabResponse as ProtobufOpenPaneInNewTabResponse,
+ OpenPluginPaneFloatingPayload,
+ OpenPluginPaneFloatingResponse as ProtobufOpenPluginPaneFloatingResponse,
OpenPluginPaneInNewTabPayload as ProtobufOpenPluginPaneInNewTabPayload,
OpenTerminalFloatingNearPluginPayload,
OpenTerminalFloatingNearPluginResponse as ProtobufOpenTerminalFloatingNearPluginResponse,
@@ -2543,6 +2545,26 @@ impl TryFrom<ProtobufPluginCommand> for PluginCommand {
},
_ => Err("Mismatched payload for ClearPaneHighlights"),
},
+ Some(CommandName::OpenPluginPaneFloating) => match protobuf_plugin_command.payload {
+ Some(Payload::OpenPluginPaneFloatingPayload(payload)) => {
+ let configuration: BTreeMap<String, String> =
+ payload.configuration.into_iter().collect();
+ let floating_pane_coordinates =
+ payload.floating_pane_coordinates.map(|f| f.into());
+ let context: BTreeMap<String, String> = payload
+ .context
+ .into_iter()
+ .map(|e| (e.name, e.value))
+ .collect();
+ Ok(PluginCommand::OpenPluginPaneFloating {
+ plugin_url: payload.plugin_url,
+ configuration,
+ floating_pane_coordinates,
+ context,
+ })
+ },
+ _ => Err("Mismatched payload for OpenPluginPaneFloating"),
+ },
None => Err("Unrecognized plugin command"),
}
}
@@ -4202,6 +4224,30 @@ impl TryFrom<PluginCommand> for ProtobufPluginCommand {
},
)),
}),
+ PluginCommand::OpenPluginPaneFloating {
+ plugin_url,
+ configuration,
+ floating_pane_coordinates,
+ context,
+ } => {
+ let context: Vec<_> = context
+ .into_iter()
+ .map(|(name, value)| ContextItem { name, value })
+ .collect();
+ let configuration: std::collections::HashMap<String, String> =
+ configuration.into_iter().collect();
+ Ok(ProtobufPluginCommand {
+ name: CommandName::OpenPluginPaneFloating as i32,
+ payload: Some(Payload::OpenPluginPaneFloatingPayload(
+ OpenPluginPaneFloatingPayload {
+ plugin_url,
+ configuration,
+ floating_pane_coordinates: floating_pane_coordinates.map(|f| f.into()),
+ context,
+ },
+ )),
+ })
+ },
}
}
}
@@ -4216,8 +4262,8 @@ use crate::data::{
OpenEditPaneInPlaceOfPaneIdResponse, OpenFileFloatingNearPluginResponse,
OpenFileFloatingResponse, OpenFileInPlaceOfPluginResponse, OpenFileInPlaceResponse,
OpenFileNearPluginResponse, OpenFileResponse, OpenPaneInNewTabResponse,
- OpenTerminalFloatingNearPluginResponse, OpenTerminalFloatingResponse,
- OpenTerminalInPlaceOfPluginResponse, OpenTerminalInPlaceResponse,
+ OpenPluginPaneFloatingResponse, OpenTerminalFloatingNearPluginResponse,
+ OpenTerminalFloatingResponse, OpenTerminalInPlaceOfPluginResponse, OpenTerminalInPlaceResponse,
OpenTerminalNearPluginResponse, OpenTerminalPaneInPlaceOfPaneIdResponse, OpenTerminalResponse,
};
@@ -4811,3 +4857,21 @@ impl From<OpenEditPaneInPlaceOfPaneIdResponse> for ProtobufOpenEditPaneInPlaceOf
}
}
}
+
+impl TryFrom<ProtobufOpenPluginPaneFloatingResponse> for OpenPluginPaneFloatingResponse {
+ type Error = &'static str;
+ fn try_from(protobuf: ProtobufOpenPluginPaneFloatingResponse) -> Result<Self, Self::Error> {
+ match protobuf.pane_id {
+ Some(pane_id) => Ok(Some(pane_id.try_into()?)),
+ None => Ok(None),
+ }
+ }
+}
+
+impl From<OpenPluginPaneFloatingResponse> for ProtobufOpenPluginPaneFloatingResponse {
+ fn from(response: OpenPluginPaneFloatingResponse) -> Self {
+ ProtobufOpenPluginPaneFloatingResponse {
+ pane_id: response.map(|p| p.try_into().unwrap()),
+ }
+ }
+}