Session-local activity shared by the Activity Center and topbar bell. Notifications, terminal bells, and meaningful program-status reports share an inbox, but retain their own lifecycle. Running work never contributes to the unread count.
- Click the always-visible topbar bell or press
<leader>ato open Inbox. - Press
<leader>hto open History directly. - Inside the widget:
Tabswitches Inbox/History,/searches,j/kor arrows navigate,Entergoes to the originating pane,ddismisses,Ctrl-ddismisses all pending entries, andvopens wrapped, scrollable message details. - Live work is grouped into independently collapsible Tasks (expanded by
default) and Commands (collapsed by default). Headers show matching counts;
click one or select it and press
Enter/Spaceto toggle.t(alsor) toggles Tasks;ctoggles Commands. Empty categories are hidden. - Search temporarily reveals matching items in collapsed categories. Expansion changes made while searching apply only to search results; leaving search restores the normal expansion state. Opening a new widget restores defaults.
- Press
yorCtrl-Cto copy the selected item's full title and message. Details also has a Copy button. Copying excludes workspace metadata, does not acknowledge the item, and works for History and Running entries too. Escleaves search or details first, then closes the widget.- The outline bell means no pending entries. A filled bell and count mean pending acknowledgement; repeated bells from one pane count as one grouped entry.
- Each item occupies one line, with a compact workspace pill on the right.
Pills use the same workspace colors and custom overrides as the workspace selector.
View and workspace pills have 8px horizontal and 3px vertical padding.
Reported task rows show application name and state, not task titles. Shell
rows show the actual command; classified tasks animate, other commands use a
static, neutral Running indicator.
Paths and full message bodies live in
vdetails, not repeated metadata rows. The search field appears only after pressing/.
Tasks show live working and blocked program reports, with progress when
available, plus shell commands matched by task rules below. A blocked task already
in Inbox is not repeated under Tasks.
Commands show shell-integrated commands when that pane has no active
program-status report. Once a command reports program status, shell fallback stays
suppressed for that command even after its status becomes idle, completes, or is
cleared. A new shell command can appear normally. Ordinary shells without either
source cannot supply task lifecycle information. Hollow does not infer a command's
expected lifetime from its name or elapsed time.
Configure rules in your Lua config. All three lists default to empty:
hollow.activity.setup({
tasks = {
"^zig build%f[%s%z]",
"^cargo test%f[%s%z]",
"^make%f[%s%z]",
"^npm run build%f[%s%z]",
},
commands = { "%-%-watch", "^npm run dev%f[%s%z]" },
ignore = { "^nvim%f[%s%z]" }, -- Hide nvim and nvim <args> entirely.
})Rules are case-sensitive Lua patterns, not regular expressions or globs,
matched against the full shell command text as received from shell integration.
^ anchors the start; %f[%s%z] matches a whitespace/end boundary so make
does not match makebelieve. Wrappers (sudo, env), paths, aliases, and compound
commands are not parsed or normalized; write patterns for the text your shell sends.
Precedence is ignore → commands → tasks:
ignore(blacklist) hides matching shell fallback entries from Tasks, Commands, Lua/CLI Running listings, and task badges. It does not terminate processes or suppress notifications, bells, or explicit program-status reports.commandsforces the collapsed Commands category, even if a task rule matches (for example, a test runner in watch mode).taskspromotes matching shell commands to expanded Tasks, with animated task indicators, including workspace badges. Full command text remains copyable.- Unmatched commands remain in Commands with a neutral static indicator.
Explicit program status always wins. Rules only affect shell fallback and cannot resurrect a command suppressed after its program status was cleared. Shell tasks leave Tasks and lose their badges when shell integration reports completion; completion does not currently create Inbox or History entries.
Each setup() replaces all rules (omitted lists become empty), copies the supplied
lists, and reclassifies already-running commands. setup() with no options resets
rules. Reloading config reapplies its rules without restarting processes or
discarding activity history.
hollow.activity.list({ view = "inbox" }) -- default view; newest first
hollow.activity.list({ view = "history" }) -- all retained entries, including pending
hollow.activity.list({ view = "running", pane_id = 42 }) -- filtered live work
hollow.activity.list({ workspace = "main", activity_kind = "program", level = "error" })
hollow.activity.running() -- live reported tasks and shell commands
hollow.activity.summary() -- { count, level? }; excludes running
hollow.activity.dismiss(entry.id) -- true if acknowledgement changed
hollow.activity.dismiss_all()
hollow.activity.clear_history() -- remove acknowledged entries only
hollow.ui.activity.open({ view = "inbox", width = 100, max_height = 24 })
hollow.ui.activity.open({ view = "history" })
-- Reusable text clipboard API; returns true when the write is queued.
hollow.clipboard.write("Text to copy")hollow.clipboard.write(text) returns false if unavailable, rejected, or text
contains an embedded NUL. It supports empty strings and multiline Unicode text.
Lists are isolated snapshots. Retained entries have numeric IDs, kind
(notification, bell, or program), title/message, severity, monotonic
timestamp_ms, pending, and a grouped occurrence count. Pane-originated
entries also retain pane ID, pane title, workspace, and cwd. Program entries
include state and optionally block_kind. Live running entries have string IDs
and optionally progress; they are not retained events and cannot be dismissed.
Live entries expose source = "program_status" or "shell_command" so callers
can distinguish them without interpreting IDs or command names. category is
"task" or "command"; a rule-classified shell task keeps source = "shell_command".
Collapsing a widget category does not remove its entries from API or CLI Running
snapshots; ignore rules do.
list accepts exact-match pane_id, workspace, activity_kind, and level
filters combined with AND. view = "running" queries the same live snapshot as
running(). The native activity CLI uses this same API;
neither Lua nor CLI listing acknowledges entries.
ui.activity.open() accepts view, width, height, max_height, theme,
chrome, and backdrop. It returns a modal handle, or nil when another modal
is already open. The existing attention_navigator action and mux attention
filter remain available for pane-oriented navigation.
- All notifications enter History, including notifications received while focused.
This includes terminal protocols and
hollow.ui.notify.*calls. - Background notifications and bells enter Inbox. Background means either the
originating pane is not focused or Hollow's window lacks OS focus. Notifications
without a pane use window focus only.
ui.notify.showacceptspane_idto associate a plugin notification with a pane. - Background
blocked,done, anderrorreports enter Inbox. Focused reports enter History only. Progress ticks and ordinaryworking/idlereports do not create history entries. Unchanged state reports update their existing entry without resurrecting a dismissed request. - Clearing or replacing a program status archives its related Inbox entry,
including
doneanderror. Starting new work does not leave an old completion marker pending. History retains the old report; unrelated program records, notifications, and bells are unaffected. Closing a pane archives its program entries as well. - Opening the widget, focusing a pane, or expiring a toast does not acknowledge the inbox. Dismissal and successful go-to-pane acknowledge the selected entry.
- Explicit toast clearing, Escape dismissal, protocol close, or notification-ID replacement archives the matching notification. Replacement retains the old version in History and creates a new entry.
- Dismissal never cancels work or clears live program status. Existing terminal tab/pane attention markers retain their normal focus-based lifecycle.
- Closed panes remain identified in history; navigation to those entries is disabled, even if a later pane reuses the same native ID.
Up to 500 events are retained; oldest events are evicted at capacity. No activity is written to disk. Config reload transfers it in memory, while application restart starts empty. Clearing history preserves pending entries.
Program-status events currently expose coalesced snapshots: intermediate transitions inside one terminal update may not be observable. History is an activity log, not a lossless protocol trace. Bells may likewise be coalesced by the terminal event loop.
Custom events activity:changed ({}) and activity:dismissed ({ id }) can be
observed through hollow.events.on. Shell command lifecycle does not emit
activity:changed; subscribe to term:command_started / term:command_finished
as well when building a custom live view. Query running() for current state.