Skip to content

Commit 5f7150d

Browse files
committed
📝 Add complete IPC examples
1 parent 26d2d1c commit 5f7150d

1 file changed

Lines changed: 156 additions & 0 deletions

File tree

src/content/docs/ipc-interface.mdx

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,162 @@ if __name__ == "__main__":
238238

239239
```
240240

241+
## Complete Code Examples
242+
243+
Below, you find two complete code examples for both opening a custom menu and observing menu interactions.
244+
245+
<Tabs>
246+
<TabItem label="Open a Custom Menu">
247+
```python title="open_menu.py"
248+
import asyncio
249+
import json
250+
import os
251+
import platform
252+
import websockets
253+
254+
def find_ipc_info_path():
255+
system = platform.system()
256+
home = os.path.expanduser("~")
257+
if system == "Windows":
258+
appdata = os.environ.get("APPDATA", os.path.join(home, "AppData", "Roaming"))
259+
return os.path.join(appdata, "kando", "ipc-info.json")
260+
elif system == "Darwin":
261+
return os.path.join(home, "Library", "Application Support", "kando", "ipc-info.json")
262+
else:
263+
flatpak_path = os.path.join(home, ".var", "app", "menu.kando.Kando", "config", "kando", "ipc-info.json")
264+
if os.path.exists(flatpak_path):
265+
return flatpak_path
266+
return os.path.join(home, ".config", "kando", "ipc-info.json")
267+
268+
async def main():
269+
info_path = find_ipc_info_path()
270+
if not os.path.exists(info_path):
271+
print(f"ipc-info.json not found at {info_path}")
272+
return
273+
274+
with open(info_path, 'r') as f:
275+
info = json.load(f)
276+
port = info['port']
277+
api_version = info['apiVersion']
278+
279+
client_api_version = 1
280+
if api_version != client_api_version:
281+
print(f"API version mismatch: server={api_version}, client={client_api_version}")
282+
return
283+
284+
uri = f"ws://127.0.0.1:{port}"
285+
menu = {
286+
"type": "submenu",
287+
"name": "Example Menu",
288+
"icon": "🌸",
289+
"iconTheme": "emoji",
290+
"children": [
291+
{"type": "simple-button", "name": "Item 1", "icon": "🚀", "iconTheme": "emoji"},
292+
{"type": "simple-button", "name": "Item 2", "icon": "💖", "iconTheme": "emoji"},
293+
{"type": "simple-button", "name": "Item 3", "icon": "🎇", "iconTheme": "emoji"},
294+
{"type": "simple-button", "name": "Item 4", "icon": "🎉", "iconTheme": "emoji"},
295+
]
296+
}
297+
298+
try:
299+
async with websockets.connect(uri) as ws:
300+
await ws.send(json.dumps({"type": "show-menu", "menu": menu}))
301+
print("Menu sent. Waiting for user interaction...")
302+
303+
async for message in ws:
304+
msg = json.loads(message)
305+
msg_type = msg.get("type")
306+
if msg_type == "select-item":
307+
path = msg.get("path", [])
308+
if path and isinstance(path[0], int) and 0 <= path[0] < 4:
309+
print(f"Selected: {menu['children'][path[0]]['icon']}")
310+
else:
311+
print(f"Selected: Unknown item (path={path})")
312+
break
313+
elif msg_type == "cancel-menu":
314+
print("Menu canceled by user.")
315+
break
316+
elif msg_type == "error":
317+
print(f"Error: {msg.get('reason')} - {msg.get('description')}")
318+
break
319+
except (ConnectionRefusedError, OSError) as e:
320+
print(f"Could not connect to Kando IPC server at {uri}: Is Kando running?")
321+
322+
if __name__ == "__main__":
323+
asyncio.run(main())
324+
325+
```
326+
</TabItem>
327+
<TabItem label="Observe Menu Interactions">
328+
```python title="observe_interaction.py"
329+
import asyncio
330+
import json
331+
import os
332+
import platform
333+
import websockets
334+
335+
def find_ipc_info_path():
336+
system = platform.system()
337+
home = os.path.expanduser("~")
338+
if system == "Windows":
339+
appdata = os.environ.get("APPDATA", os.path.join(home, "AppData", "Roaming"))
340+
return os.path.join(appdata, "kando", "ipc-info.json")
341+
elif system == "Darwin":
342+
return os.path.join(home, "Library", "Application Support", "kando", "ipc-info.json")
343+
else:
344+
flatpak_path = os.path.join(home, ".var", "app", "menu.kando.Kando", "config", "kando", "ipc-info.json")
345+
if os.path.exists(flatpak_path):
346+
return flatpak_path
347+
return os.path.join(home, ".config", "kando", "ipc-info.json")
348+
349+
async def main():
350+
info_path = find_ipc_info_path()
351+
if not os.path.exists(info_path):
352+
print(f"ipc-info.json not found at {info_path}")
353+
return
354+
355+
with open(info_path, 'r') as f:
356+
info = json.load(f)
357+
port = info['port']
358+
api_version = info['apiVersion']
359+
360+
client_api_version = 1
361+
if api_version != client_api_version:
362+
print(f"API version mismatch: server={api_version}, client={client_api_version}")
363+
return
364+
365+
uri = f"ws://127.0.0.1:{port}"
366+
try:
367+
async with websockets.connect(uri) as ws:
368+
await ws.send(json.dumps({"type": "start-observing"}))
369+
print("Started observing menu events...")
370+
371+
try:
372+
async for message in ws:
373+
msg = json.loads(message)
374+
msg_type = msg.get("type")
375+
if msg_type == "open-menu":
376+
print("Menu opened")
377+
elif msg_type == "cancel-menu":
378+
print("Menu canceled")
379+
elif msg_type == "select-item":
380+
print(f"Item selected: path={msg.get('path')} target={msg.get('target')}")
381+
elif msg_type == "hover-item":
382+
print(f"Item hovered: path={msg.get('path')} target={msg.get('target')}")
383+
elif msg_type == "error":
384+
print(f"Error: {msg.get('reason')} - {msg.get('description')}")
385+
except websockets.ConnectionClosed:
386+
print("Connection closed.")
387+
except (ConnectionRefusedError, OSError) as e:
388+
print(f"Could not connect to Kando IPC server at {uri}: Is Kando running?")
389+
390+
if __name__ == "__main__":
391+
asyncio.run(main())
392+
393+
```
394+
</TabItem>
395+
</Tabs>
396+
241397
## Final Notes
242398

243399
Have you created something cool with the IPC API?

0 commit comments

Comments
 (0)