From 2c7c8beb079c5fd58a2e53efd2058df050ad9f55 Mon Sep 17 00:00:00 2001 From: Ogulcan Celik Date: Wed, 15 Jul 2026 03:58:07 +0300 Subject: feat: add floating popup panes refs #1125 --- docs/next/CHANGELOG.md | 1 + docs/next/api/herdr-api.schema.json | 88 ++++++++++++++++++++++ .../website/src/content/docs/cli-reference.mdx | 9 ++- .../website/src/content/docs/configuration.mdx | 14 +++- docs/next/website/src/content/docs/plugins.mdx | 27 +++++-- docs/next/website/src/content/docs/socket-api.mdx | 21 ++++-- 6 files changed, 144 insertions(+), 16 deletions(-) (limited to 'docs/next') diff --git a/docs/next/CHANGELOG.md b/docs/next/CHANGELOG.md index 51407fed..a3894b34 100644 --- a/docs/next/CHANGELOG.md +++ b/docs/next/CHANGELOG.md @@ -41,6 +41,7 @@ - Added `herdr terminal session control` for bridge processes that need live ANSI frames plus input, resize, scroll, release, and takeover authority. - Added `ui.hide_tab_bar_when_single_tab` to hide the tab row when a workspace has one tab. (#448) - Added Japanese and Simplified Chinese website docs. +- Added session-modal popup terminal panes for custom command keybindings and plugin panes, so tools such as `lazygit` or plugin pickers can open without changing the tab layout. (#1125) ### Changed - The mobile switcher now starts from an agents-first summary and renders worktrees as a tree, making narrow terminals easier to scan. diff --git a/docs/next/api/herdr-api.schema.json b/docs/next/api/herdr-api.schema.json index d146e11b..a9712784 100644 --- a/docs/next/api/herdr-api.schema.json +++ b/docs/next/api/herdr-api.schema.json @@ -3052,6 +3052,16 @@ "default": false, "type": "boolean" }, + "height": { + "anyOf": [ + { + "$ref": "#/schemas/request/$defs/PopupSize" + }, + { + "type": "null" + } + ] + }, "placement": { "anyOf": [ { @@ -3071,6 +3081,16 @@ "null" ] }, + "width": { + "anyOf": [ + { + "$ref": "#/schemas/request/$defs/PopupSize" + }, + { + "type": "null" + } + ] + }, "workspace_id": { "type": [ "string", @@ -3087,6 +3107,7 @@ "PluginPanePlacement": { "enum": [ "overlay", + "popup", "split", "tab", "zoomed" @@ -3175,6 +3196,21 @@ ], "type": "object" }, + "PopupSize": { + "oneOf": [ + { + "description": "Outer popup size in terminal cells, including the border.", + "maximum": 65535, + "minimum": 0, + "type": "integer" + }, + { + "description": "Outer popup size as a percentage of the terminal area, for example 80%.", + "pattern": "^(100|[1-9][0-9]?)%$", + "type": "string" + } + ] + }, "ReadFormat": { "enum": [ "text", @@ -5027,6 +5063,22 @@ ], "type": "object" }, + { + "properties": { + "method": { + "const": "popup.close", + "type": "string" + }, + "params": { + "$ref": "#/schemas/request/$defs/EmptyParams" + } + }, + "required": [ + "method", + "params" + ], + "type": "object" + }, { "properties": { "method": { @@ -7756,6 +7808,16 @@ "null" ] }, + "height": { + "anyOf": [ + { + "$ref": "#/schemas/success_response/$defs/PopupSize" + }, + { + "type": "null" + } + ] + }, "id": { "type": "string" }, @@ -7774,6 +7836,16 @@ }, "title": { "type": "string" + }, + "width": { + "anyOf": [ + { + "$ref": "#/schemas/success_response/$defs/PopupSize" + }, + { + "type": "null" + } + ] } }, "required": [ @@ -7805,6 +7877,7 @@ "PluginPanePlacement": { "enum": [ "overlay", + "popup", "split", "tab", "zoomed" @@ -7879,6 +7952,21 @@ ], "type": "string" }, + "PopupSize": { + "oneOf": [ + { + "description": "Outer popup size in terminal cells, including the border.", + "maximum": 65535, + "minimum": 0, + "type": "integer" + }, + { + "description": "Outer popup size as a percentage of the terminal area, for example 80%.", + "pattern": "^(100|[1-9][0-9]?)%$", + "type": "string" + } + ] + }, "ReadFormat": { "enum": [ "text", diff --git a/docs/next/website/src/content/docs/cli-reference.mdx b/docs/next/website/src/content/docs/cli-reference.mdx index 57db7484..a7c11d92 100644 --- a/docs/next/website/src/content/docs/cli-reference.mdx +++ b/docs/next/website/src/content/docs/cli-reference.mdx @@ -404,7 +404,7 @@ herdr plugin log list [--plugin ID] [--limit N] Managed terminal panes: ```bash -herdr plugin pane open --plugin ID --entrypoint ID [--placement overlay|split|tab|zoomed] [--workspace ID] [--target-pane PANE] [--direction right|down] [--cwd PATH] [--env KEY=VALUE] [--focus|--no-focus] +herdr plugin pane open --plugin ID --entrypoint ID [--placement overlay|popup|split|tab|zoomed] [--width SIZE] [--height SIZE] [--workspace ID] [--target-pane PANE] [--direction right|down] [--cwd PATH] [--env KEY=VALUE] [--focus|--no-focus] herdr plugin pane focus herdr plugin pane close ``` @@ -413,7 +413,12 @@ herdr plugin pane close with the current platform. It starts a manifest-declared `[[panes]]` command as a Herdr-managed terminal pane. The manifest default is `overlay`, which opens a temporary zoomed overlay over the active pane. It can also open as a split, a -new tab, or a zoomed pane. Native non-terminal plugin panes are a later surface. +new tab, a zoomed pane, or a session-modal `popup` that does not change the tab +layout. `--width` and `--height` set the outer popup dimensions in terminal +cells or percentages such as `80%`; omitted dimensions default to half the +terminal size, and values smaller than the popup minimum are clamped. A popup +is not a Herdr pane, does not export `HERDR_PANE_ID`, and does not participate +in pane or agent APIs. Native non-terminal plugin panes are a later surface. `--env KEY=VALUE` can be repeated on process-launching commands. It applies to the newly launched process only. Herdr-managed variables such as `HERDR_SOCKET_PATH`, `HERDR_BIN_PATH`, `HERDR_ENV`, `HERDR_WORKSPACE_ID`, `HERDR_TAB_ID`, `HERDR_PANE_ID`, `HERDR_PLUGIN_ID`, `HERDR_PLUGIN_ROOT`, `HERDR_PLUGIN_CONFIG_DIR`, `HERDR_PLUGIN_STATE_DIR`, `HERDR_PLUGIN_ENTRYPOINT_ID`, and `HERDR_PLUGIN_CONTEXT_JSON` stay authoritative when they conflict with caller-provided env. diff --git a/docs/next/website/src/content/docs/configuration.mdx b/docs/next/website/src/content/docs/configuration.mdx index 01d82547..0cae754c 100644 --- a/docs/next/website/src/content/docs/configuration.mdx +++ b/docs/next/website/src/content/docs/configuration.mdx @@ -163,12 +163,22 @@ Custom commands use the same keybinding syntax. ```toml [[keys.command]] key = "prefix+alt+g" -type = "pane" +type = "popup" command = "lazygit" description = "run lazygit" +width = "80%" +height = "80%" ``` -`type = "pane"` opens a temporary pane and closes it when the command exits. +`type = "popup"` opens a session-modal popup without changing the tab layout. +The popup receives all terminal input, including Escape, until its command +exits. `width` and `height` are optional; omit them for the default half-size +popup, use numbers for terminal cells, or use strings like `"80%"` for a +percentage of the terminal area. Dimensions include the popup border, and +values smaller than the popup minimum are clamped. Popup commands do not receive +`HERDR_PANE_ID`; use `HERDR_ACTIVE_PANE_ID` for the underlying tiled pane. + +`type = "pane"` opens a temporary zoomed pane and closes it when the command exits. `type = "shell"` runs detached in the background. diff --git a/docs/next/website/src/content/docs/plugins.mdx b/docs/next/website/src/content/docs/plugins.mdx index 9f57ff47..e572ef10 100644 --- a/docs/next/website/src/content/docs/plugins.mdx +++ b/docs/next/website/src/content/docs/plugins.mdx @@ -259,12 +259,27 @@ while Windows clients connect to a named pipe. CLI calls through Manifest pane `placement` defaults to `overlay`, which opens a temporary zoomed overlay over the active pane and restores the previous focus and zoom when it closes. A `plugin.pane.open` request can override the manifest placement with -`overlay`, `split`, `tab`, or `zoomed`. - -Plugin panes are normal Herdr panes after they open. Plugins can call standard -pane APIs such as `pane.move`, `pane.swap`, `pane.resize`, and `pane.zoom` -through the socket or CLI; Herdr keeps plugin pane ownership attached to the -underlying pane when it moves across tabs or workspaces. +`overlay`, `popup`, `split`, `tab`, or `zoomed`. + +`placement = "popup"` opens a session-modal terminal popup without changing the +tiled layout. It accepts optional `width` and `height` fields in the manifest or +open request; omit them for the default half-size popup, use numbers for outer +terminal-cell dimensions, or use strings like `"80%"` for a percentage of the +terminal area. It receives all terminal input, including Escape, and closes +when the command exits or a `popup.close` request is sent. Dimensions smaller +than the popup minimum are clamped. + +Split, tab, zoomed, and overlay plugin panes are normal Herdr panes after they +open. Plugins can call standard pane APIs such as `pane.move`, `pane.swap`, +`pane.resize`, and `pane.zoom` through the socket or CLI; Herdr keeps plugin +pane ownership attached to the underlying pane when it moves across tabs or +workspaces. A popup is a singleton session resource rather than a Herdr pane: +it has no pane ID, does not change plugin focus context, emits no pane lifecycle +events, and does not participate in pane, layout, persistence, or agent APIs. +Its process does not receive `HERDR_PANE_ID`; the underlying tiled pane remains +available through `HERDR_PLUGIN_CONTEXT_JSON`. +Opening a popup returns `ui_busy` while Settings, Copy mode, or another Herdr +modal is active, and `plugin.pane.open` returns an `ok` result after launch. On Windows, build commands, action commands, and event commands resolve common `PATHEXT` shims such as `npm.cmd`, `bun.cmd`, and `pnpm.cmd` when the bare diff --git a/docs/next/website/src/content/docs/socket-api.mdx b/docs/next/website/src/content/docs/socket-api.mdx index e914eabc..cc8d5675 100644 --- a/docs/next/website/src/content/docs/socket-api.mdx +++ b/docs/next/website/src/content/docs/socket-api.mdx @@ -104,6 +104,7 @@ Raw socket method names use dot notation: | Worktree | `worktree.list`, `worktree.create`, `worktree.open`, `worktree.remove` | | Tab | `tab.create`, `tab.list`, `tab.get`, `tab.focus`, `tab.rename`, `tab.move`, `tab.close` | | Pane | `pane.split`, `pane.swap`, `pane.move`, `pane.zoom`, `pane.layout`, `pane.process_info`, `pane.neighbor`, `pane.edges`, `pane.focus_direction`, `pane.resize`, `pane.list`, `pane.current`, `pane.get`, `pane.rename`, `pane.send_text`, `pane.send_keys`, `pane.send_input`, `pane.read`, `pane.graphics.info`, `pane.graphics.set`, `pane.graphics.clear`, `pane.graphics.stream`, `pane.report_agent`, `pane.report_agent_session`, `pane.report_metadata`, `pane.clear_agent_authority`, `pane.release_agent`, `pane.close`, `pane.wait_for_output` | +| Popup | `popup.close` | | Layout | `layout.export`, `layout.apply`, `layout.set_split_ratio` | | Agent | `agent.list`, `agent.get`, `agent.read`, `agent.explain`, `agent.send`, `agent.rename`, `agent.focus`, `agent.start` | | Events | `events.subscribe`, `events.wait` | @@ -504,12 +505,20 @@ Open a managed terminal UI: `plugin.pane.open` requires an installed, enabled, platform-compatible plugin, then launches the requested manifest `[[panes]]` entrypoint as an argv-backed terminal pane. Manifest pane `placement` defaults to `overlay`; request -`placement` overrides the manifest with `overlay`, `split`, `tab`, or `zoomed`. -Overlay panes target the active pane. Split and zoomed panes target an existing -pane; tab panes can target a workspace. The pane -behaves like a normal Herdr pane, but `plugin.pane.focus` and -`plugin.pane.close` only operate on panes opened through the plugin API. Focus -returns `plugin_pane_focused`; close returns `plugin_pane_closed`. +`placement` overrides the manifest with `overlay`, `popup`, `split`, `tab`, or +`zoomed`. Overlay and popup placements use the active tiled pane as launch +context. Popup terminals are session-modal and do not change the tab layout; +optional `width` and `height` fields set their outer size as terminal cells or +percentages such as `"80%"`. Omitted dimensions default to half the terminal +size, with too-small values clamped to the popup minimum. A popup has no pane +ID, remains outside all `pane.*` and agent APIs, emits no pane lifecycle events, +leaves plugin focus context on the underlying tiled pane, and does not export +`HERDR_PANE_ID` to its process. Popup launch returns `ok`; `popup.close` closes +the active popup and returns +`popup_not_open` when none exists. Split and zoomed panes target an existing +pane; tab panes can target a workspace. Split, tab, zoomed, and overlay panes +behave like normal Herdr panes, and `plugin.pane.focus` and `plugin.pane.close` +continue to operate on those panes. ## Socket transport -- cgit v1.2.3