@@ -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
243399Have you created something cool with the IPC API?
0 commit comments