QUERY_AND_SUBSCRIBE_FORMAT.md

Query and Subscribe Format

Paneru exposes structured state over the same IPC channel used by send-cmd: a Mach service named com.github.karinushka.paneru. The CLI commands below require a running Paneru daemon.

The JSON below is what the CLI prints, not what crosses between processes. Requests and responses travel as typed values in a compact binary encoding (postcard); paneru query and paneru subscribe render them as JSON because a terminal — and jq, and a status bar's shell script — needs text. Anything consuming these commands' output sees exactly the shapes documented here.

A client written in Rust can skip the JSON entirely by using the paneru::types module (src/types): its wire::Request and wire::Response are the protocol, and async-mach-ports is the transport.

All query responses are a single JSON document. subscribe emits line-delimited JSON, with one complete event object per line.

Query Commands

shell
1paneru query state --json
2paneru query virtual-workspaces --json
3paneru query active --json

--json is accepted for clarity and is the only output format, so it may be omitted; callers should include it anyway, in case another format is ever added.

paneru query state --json

Returns the complete state document.

json
1{
2 "version": 1,
3 "timestamp": 1777740000,
4 "active": {
5 "display_id": 1,
6 "native_workspace_id": 4,
7 "virtual_workspace_number": 3,
8 "focused_window_id": 321,
9 "focused_bundle_id": "com.apple.Terminal",
10 "focused_app_name": "Terminal",
11 "focused_window_title": "paneru"
12 },
13 "virtual_workspaces": [
14 {
15 "number": 1,
16 "native_workspace_id": 4,
17 "active": false,
18 "windows": []
19 },
20 {
21 "number": 2,
22 "native_workspace_id": 4,
23 "active": false,
24 "windows": []
25 },
26 {
27 "number": 3,
28 "native_workspace_id": 4,
29 "active": true,
30 "windows": [
31 {
32 "window_id": 321,
33 "bundle_id": "com.apple.Terminal",
34 "app_name": "Terminal",
35 "title": "paneru",
36 "role": "AXWindow",
37 "subrole": "AXStandardWindow",
38 "focused": true,
39 "floating": false
40 }
41 ]
42 }
43 ]
44}

paneru query virtual-workspaces --json

Returns only the virtual_workspaces array from the complete state document.

json
1[
2 {
3 "number": 1,
4 "native_workspace_id": 4,
5 "active": false,
6 "windows": []
7 },
8 {
9 "number": 2,
10 "native_workspace_id": 4,
11 "active": false,
12 "windows": []
13 },
14 {
15 "number": 3,
16 "native_workspace_id": 4,
17 "active": true,
18 "windows": [
19 {
20 "window_id": 321,
21 "bundle_id": "com.apple.Terminal",
22 "app_name": "Terminal",
23 "title": "paneru",
24 "role": "AXWindow",
25 "subrole": "AXStandardWindow",
26 "focused": true,
27 "floating": false
28 }
29 ]
30 }
31]

paneru query active --json

Returns only the active display, workspace, and focused-window state.

json
1{
2 "display_id": 1,
3 "native_workspace_id": 4,
4 "virtual_workspace_number": 3,
5 "focused_window_id": 321,
6 "focused_bundle_id": "com.apple.Terminal",
7 "focused_app_name": "Terminal",
8 "focused_window_title": "paneru"
9}

Fields

FieldTypeDescription
versionnumberState document format version. Currently 1.
timestampnumberUnix timestamp in seconds when the response was built.
activeobjectCurrent active display/native workspace/virtual workspace/focused window.
display_idnumber or nullCoreGraphics display id for the active display, when known.
native_workspace_idnumber or nullmacOS Space id for the active native workspace, when known.
virtual_workspace_numbernumber or nullOne-based Paneru virtual workspace number, when known.
focused_window_idnumber or nullFocused window id, when known.
focused_bundle_idstring or nullBundle id of the focused window's app, when known.
focused_app_namestring or nullDisplay name of the focused window's app, when known.
focused_window_titlestring or nullTitle of the focused window, when known.
virtual_workspacesarrayVirtual workspace rows known to Paneru.
numbernumberOne-based virtual workspace number.
activebooleanWhether this virtual workspace is currently selected.
windowsarrayManaged windows in this virtual workspace row.
window_idnumberWindow id.
bundle_idstringBundle id for the owning application, or an empty string if unknown.
app_namestringDisplay name for the owning application, or an empty string if unknown.
titlestringWindow title, or an empty string if unknown.
rolestring or nullAccessibility role (e.g. AXWindow), or null if the window reports none.
subrolestring or nullAccessibility subrole (e.g. AXStandardWindow, AXDialog, AXFloatingWindow), or null if the window reports none.
focusedbooleanWhether this window is focused.
floatingbooleanWhether this window is unmanaged/floating.

Paneru may include empty windows arrays for missing virtual workspace numbers inside a native workspace so integrations can render stable numbered slots.

Subscribe Command

shell
1paneru subscribe --json

subscribe keeps its channel open and writes one JSON event per line. The stream is intended for integrations such as SketchyBar, so it emits changes that are useful for keeping a bar in sync: focus changes, native or virtual workspace changes, managed window-list changes, window title changes, and display changes. Paneru coalesces duplicate internal events from the same ECS tick and skips events whose relevant state has not changed since the last emitted event. Consumers should parse each line independently and then call paneru query state --json when they need a full refresh.

Event Types

json
1{"event":"virtual_workspace_changed","active":{"display_id":1,"native_workspace_id":4,"virtual_workspace_number":3,"focused_window_id":321,"focused_bundle_id":"com.apple.Terminal","focused_app_name":"Terminal","focused_window_title":"paneru"}}

Emitted after native Space changes and Paneru virtual workspace switches. Paneru derives this from both incoming workspace events and ECS active-workspace marker changes, so integrations receive the event when the visible workspace state changes.

json
1{"event":"windows_changed","virtual_workspace_number":3,"active":{"display_id":1,"native_workspace_id":4,"virtual_workspace_number":3,"focused_window_id":321,"focused_bundle_id":"com.apple.Terminal","focused_app_name":"Terminal","focused_window_title":"paneru"}}

Emitted after managed window creation/destruction/minimize/deminimize events and after Paneru moves or sends a window between virtual workspaces. The event is emitted only when Paneru's virtual workspace/window state differs from the last emitted windows_changed event.

json
1{"event":"window_focused","window_id":321,"bundle_id":"com.apple.Terminal","title":"paneru","virtual_workspace_number":3}

Emitted when focus changes. Paneru derives this from both incoming focus events and ECS focused-window marker changes, so internally handled focus transitions are visible to subscribers. The window_id, bundle_id, title, and virtual_workspace_number fields are taken from the final active state for the tick, so stale lower-level focus notifications are not forwarded with mismatched window metadata.

json
1{"event":"window_title_changed","window_id":321,"title":"paneru"}

Emitted when a window title changes.

json
1{"event":"display_changed","display_id":1}

Emitted when display configuration changes. display_id can be null when the event is a global display-change notification and Paneru cannot resolve an active display id.

Virtual Workspace Commands

Absolute virtual workspace selection is addressed as a window command:

shell
1paneru send-cmd window virtualnum 3
2paneru send-cmd window virtualmovenum 3
3paneru send-cmd window virtualsendnum 3

The matching config binding names are:

toml
1[bindings]
2window_virtualnum_3 = "cmd + alt - 3"
3window_virtualmovenum_3 = "cmd + alt + ctrl - 3"
4window_virtualsendnum_3 = "cmd + alt + shift - 3"