Skip to content

Commit ca13934

Browse files
committed
📝 Update IPC interface docs
1 parent 297d648 commit ca13934

1 file changed

Lines changed: 68 additions & 59 deletions

File tree

src/content/docs/ipc-interface.mdx

Lines changed: 68 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,11 @@ The location of this file depends on the operating system and installation metho
6060
```json
6161
{
6262
"port": 12345,
63-
"apiVersion": 1
63+
"apiVersion": 2
6464
}
6565
```
6666

67-
Your client should always check this file before connecting, as the port may change between Kando sessions.
67+
Your client should always check this file before connecting, as the port will change between Kando sessions.
6868

6969
### Example code to find `ipc-info.json`
7070

@@ -100,35 +100,34 @@ Once you have the connection info, you can connect to the WebSocket server and s
100100
To open a custom menu in Kando and receive the user's selection, connect to the WebSocket server and send a `show-menu` message.
101101
Kando will display your menu and send back events as the user interacts.
102102

103+
<Aside type="caution" title="Version Compatibility">
104+
With Kando 3.0, the IPC API has been updated to version 2. Previously, there were `select-item`, `hover-item`, and `cancel-menu` messages. In version 2, these have been replaced by a single `menu-interaction` message that provides more detailed information about the user's actions.
105+
</Aside>
106+
103107
### Available Messages
104108

105109
| Message&nbsp;Name | Sender | Description |
106110
|-----------------|-----------|--------------------------------------------------|
107-
| `show-menu` | Client | Request to show a custom menu. Pass the menu structure via the `menu` parameter as JSON. **The format of the menu is the same as the one used in the `menus.json` file.** See [Menu Configuration](/config-files/#menu-item-descriptions) for more details. |
108-
| `select-item` | Server | The selected item is passed via the `path` and `target` parameters. These describe its position in the menu tree and the type of item which was selected. See below for an example. |
109-
| `hover-item` | Server | Use this to get notified when the user hovers over a menu item. The same parameters are used as in `select-item`. |
110-
| `cancel-menu` | Server | Triggered when user closed the menu without a selection. |
111-
| `error` | Server | An error occurred. Inspect the `reason` and `description` properties for more details. |
111+
| `show-menu` | Client | Request to show a custom menu. Pass the menu structure via the `menu` parameter as JSON. **The format of the menu is the same as the one used in the `menus.json` file.** See [Menu Configuration](/config-files/#menu-item-descriptions) for more details. |
112+
| `menu-interaction` | Server | The selected item is passed via the `path` parameter. The type of interaction via the `interaction` parameter. See below for details. |
113+
| `error` | Server | An error occurred. Inspect the `reason` and `description` properties for more details. |
112114

113-
#### Message Arguments: `path` and `target`
115+
#### Message Arguments: `path` and `interaction`
114116

115-
Some messages (such as `select-item` and `hover-item`) include the arguments `path` and `target`:
117+
The menu-interaction message contains two important arguments: `path` and `interaction`.
116118

117119
**`path`** is an array of zero-based integers representing the position of the item in the menu tree. For example, `[2, 1]` means the third child of the root, then the second child of that submenu.
118120

119-
**`target`** indicates what kind of menu element was interacted with. Possible values are:
120-
- `item`: A regular final menu item (button). Selection of this item usually triggers an action and closes the menu.
121-
- `submenu`: A submenu which contains other items. Selection of this item opens the submenu and does not close the menu.
122-
- `parent`: The parent menu of the currently opened submenu. If this is selected, the current submenu will be closed and the parent menu will be shown.
121+
**`interaction`** indicates what the user did. It can be one of the following: `'openMenu'`, `'openSubmenu'`, `'closeMenu'`, `'closeSubmenu'`, `'hoverParent'`, `'hoverCenter'`, `'hoverButton'`, `'hoverSubmenu'`, `'selectButton'`, `'activateMenu'`.
123122

124123
**Example**
125124

126125
This means the user selected the third item in the first submenu.
127126

128127
```json
129128
{
130-
"type": "select-item",
131-
"target": "item",
129+
"type": "menu-interaction",
130+
"interaction": "selectButton",
132131
"path": [0, 2]
133132
}
134133
```
@@ -140,6 +139,10 @@ The code snippet below demonstrates how to open a custom menu in Kando using Pyt
140139
It reads the connection info from `ipc-info.json`, connects to the WebSocket server, and sends a `show-menu` message with a simple menu structure.
141140
It then listens for user interactions and prints the selected item or cancellation.
142141

142+
<Aside type="note" title="Complete Code Examples">
143+
At the bottom of the page, you can find [a complete code example](#complete-code-examples) that includes error handling and API version checking.
144+
</Aside>
145+
143146
```python title="open_menu.py"
144147
import asyncio
145148
import json
@@ -152,13 +155,16 @@ async def main():
152155
uri = f"ws://127.0.0.1:{info['port']}"
153156

154157
menu = {
155-
"type": "submenu",
158+
"type": "root",
156159
"name": "Example Menu",
157160
"icon": "🌸",
158161
"iconTheme": "emoji",
162+
"activateWorkflow": {"actions": [{"type": "close-menu"}]},
159163
"children": [
160-
{"type": "simple-button", "name": "Item 1", "icon": "🚀", "iconTheme": "emoji"},
161-
{"type": "simple-button", "name": "Item 2", "icon": "💖", "iconTheme": "emoji"},
164+
{"type": "button", "name": "Item 1", "icon": "🚀", "iconTheme": "emoji", "selectWorkflow": {"actions": [{"type": "close-menu"}]}},
165+
{"type": "button", "name": "Item 2", "icon": "💖", "iconTheme": "emoji", "selectWorkflow": {"actions": [{"type": "close-menu"}]}},
166+
{"type": "button", "name": "Item 3", "icon": "🎇", "iconTheme": "emoji", "selectWorkflow": {"actions": [{"type": "close-menu"}]}},
167+
{"type": "button", "name": "Item 4", "icon": "🎉", "iconTheme": "emoji", "selectWorkflow": {"actions": [{"type": "close-menu"}]}},
162168
]
163169
}
164170

@@ -168,12 +174,15 @@ async def main():
168174

169175
async for message in ws:
170176
msg = json.loads(message)
171-
if msg.get("type") == "select-item":
172-
print(f"Selected: {msg.get('path')}")
173-
break
174-
elif msg.get("type") == "cancel-menu":
175-
print("Menu canceled by user.")
176-
break
177+
if msg.get("type") == "menu-interaction":
178+
path = msg.get("path", [])
179+
interaction = msg.get("interaction")
180+
181+
if interaction == "selectButton":
182+
print(f"Selected: {menu['children'][path[0]]['icon']}")
183+
break
184+
else:
185+
print(f"Interaction: {interaction} at path {path}")
177186

178187
if __name__ == "__main__":
179188
asyncio.run(main())
@@ -182,27 +191,32 @@ if __name__ == "__main__":
182191

183192
## Listening to Menu Interactions
184193

185-
To observe all menu interactions (open, hover, select, cancel), of any opened menu, connect to the WebSocket server and send a `start-observing` message.
194+
To observe all menu interactions (open, hover, select, cancel), of any menu opened by Kando, connect to the WebSocket server and send a `start-observing` message.
186195
Kando will then send you events as they happen.
187196
If you want to stop receiving events, send a `stop-observing` message.
188197

198+
<Aside type="caution" title="Version Compatibility">
199+
With Kando 3.0, the IPC API has been updated to version 2. Previously, there were `select-item`, `hover-item`, and `cancel-menu` messages. In version 2, these have been replaced by a single `menu-interaction` message that provides more detailed information about the user's actions.
200+
</Aside>
201+
189202
### Available Messages
190203

191204
| Message&nbsp;Name | Sender | Description |
192205
|-------------------|-----------|-----------------------------------------------------|
193206
| `start-observing` | Client | Send this to start receiving menu interactions. events. |
194207
| `stop-observing` | Client | Send this to stop receiving menu interaction events. |
195-
| `open-menu` | Server | This will be sent by Kando when a menu is opened. |
196-
| `hover-item` | Server | _Same as above_ |
197-
| `select-item` | Server | _Same as above_ |
198-
| `cancel-menu` | Server | _Same as above_ |
208+
| `menu-interaction` | Server | _Same as above_ |
199209
| `error` | Server | _Same as above_ |
200210

201211
### Example code to observe menu interactions
202212

203213
The code snippet below demonstrates how to observe menu interactions in Kando using Python.
204214
It connects to the WebSocket server, sends a `start-observing` message, and prints events as they happen (menu opened, item hovered, item selected, menu canceled).
205215

216+
<Aside type="note" title="Complete Code Examples">
217+
At the bottom of the page, you can find [a complete code example](#complete-code-examples) that includes error handling and API version checking.
218+
</Aside>
219+
206220
```python title="observe_interaction.py"
207221
import asyncio
208222
import json
@@ -220,14 +234,13 @@ async def main():
220234

221235
async for message in ws:
222236
msg = json.loads(message)
223-
if msg.get("type") == "open-menu":
224-
print("Menu opened")
225-
elif msg.get("type") == "hover-item":
226-
print(f"Item hovered: {msg.get('path')} target={msg.get('target')}")
227-
elif msg.get("type") == "select-item":
228-
print(f"Item selected: {msg.get('path')} target={msg.get('target')}")
229-
elif msg.get("type") == "cancel-menu":
230-
print("Menu canceled")
237+
msg_type = msg.get("type")
238+
if msg_type == "menu-interaction":
239+
interaction = msg.get("interaction")
240+
path = msg.get("path", [])
241+
print(f"Interaction: {interaction} at path {path}")
242+
elif msg_type == "error":
243+
print(f"Error: {msg.get('reason')} - {msg.get('description')}")
231244

232245
if __name__ == "__main__":
233246
asyncio.run(main())
@@ -272,22 +285,23 @@ Below, you find two complete code examples for both opening a custom menu and ob
272285
port = info['port']
273286
api_version = info['apiVersion']
274287

275-
client_api_version = 1
288+
client_api_version = 2
276289
if api_version != client_api_version:
277290
print(f"API version mismatch: server={api_version}, client={client_api_version}")
278291
return
279292

280293
uri = f"ws://127.0.0.1:{port}"
281294
menu = {
282-
"type": "submenu",
295+
"type": "root",
283296
"name": "Example Menu",
284297
"icon": "🌸",
285298
"iconTheme": "emoji",
299+
"activateWorkflow": {"actions": [{"type": "close-menu"}]},
286300
"children": [
287-
{"type": "simple-button", "name": "Item 1", "icon": "🚀", "iconTheme": "emoji"},
288-
{"type": "simple-button", "name": "Item 2", "icon": "💖", "iconTheme": "emoji"},
289-
{"type": "simple-button", "name": "Item 3", "icon": "🎇", "iconTheme": "emoji"},
290-
{"type": "simple-button", "name": "Item 4", "icon": "🎉", "iconTheme": "emoji"},
301+
{"type": "button", "name": "Item 1", "icon": "🚀", "iconTheme": "emoji", "selectWorkflow": {"actions": [{"type": "close-menu"}]}},
302+
{"type": "button", "name": "Item 2", "icon": "💖", "iconTheme": "emoji", "selectWorkflow": {"actions": [{"type": "close-menu"}]}},
303+
{"type": "button", "name": "Item 3", "icon": "🎇", "iconTheme": "emoji", "selectWorkflow": {"actions": [{"type": "close-menu"}]}},
304+
{"type": "button", "name": "Item 4", "icon": "🎉", "iconTheme": "emoji", "selectWorkflow": {"actions": [{"type": "close-menu"}]}},
291305
]
292306
}
293307

@@ -299,16 +313,15 @@ Below, you find two complete code examples for both opening a custom menu and ob
299313
async for message in ws:
300314
msg = json.loads(message)
301315
msg_type = msg.get("type")
302-
if msg_type == "select-item":
316+
if msg_type == "menu-interaction":
303317
path = msg.get("path", [])
304-
if path and isinstance(path[0], int) and 0 <= path[0] < 4:
318+
interaction = msg.get("interaction")
319+
320+
if interaction == "selectButton":
305321
print(f"Selected: {menu['children'][path[0]]['icon']}")
322+
break
306323
else:
307-
print(f"Selected: Unknown item (path={path})")
308-
break
309-
elif msg_type == "cancel-menu":
310-
print("Menu canceled by user.")
311-
break
324+
print(f"Interaction: {interaction} at path {path}")
312325
elif msg_type == "error":
313326
print(f"Error: {msg.get('reason')} - {msg.get('description')}")
314327
break
@@ -353,7 +366,7 @@ Below, you find two complete code examples for both opening a custom menu and ob
353366
port = info['port']
354367
api_version = info['apiVersion']
355368

356-
client_api_version = 1
369+
client_api_version = 2
357370
if api_version != client_api_version:
358371
print(f"API version mismatch: server={api_version}, client={client_api_version}")
359372
return
@@ -368,14 +381,10 @@ Below, you find two complete code examples for both opening a custom menu and ob
368381
async for message in ws:
369382
msg = json.loads(message)
370383
msg_type = msg.get("type")
371-
if msg_type == "open-menu":
372-
print("Menu opened")
373-
elif msg_type == "cancel-menu":
374-
print("Menu canceled")
375-
elif msg_type == "select-item":
376-
print(f"Item selected: path={msg.get('path')} target={msg.get('target')}")
377-
elif msg_type == "hover-item":
378-
print(f"Item hovered: path={msg.get('path')} target={msg.get('target')}")
384+
if msg_type == "menu-interaction":
385+
interaction = msg.get("interaction")
386+
path = msg.get("path", [])
387+
print(f"Interaction: {interaction} at path {path}")
379388
elif msg_type == "error":
380389
print(f"Error: {msg.get('reason')} - {msg.get('description')}")
381390
except websockets.ConnectionClosed:

0 commit comments

Comments
 (0)