purview_gpui/ext.rs
1//! [`PurviewAppExt`]: reach the installed [`AppHandle`] from any gpui context.
2
3use std::net::SocketAddr;
4
5use gpui::App;
6use purview::AppHandle;
7
8use crate::install::Installed;
9
10/// Extension on gpui's [`App`] (and thus any `Context<V>`, which derefs to it)
11/// to reach the bridge's [`AppHandle`] — e.g. `cx.purview().open("Detail")` to
12/// open another projected window from inside a view or handler.
13pub trait PurviewAppExt {
14 /// The installed bridge's [`AppHandle`].
15 ///
16 /// # Panics
17 ///
18 /// Panics if no bridge was installed on this `App` (see
19 /// [`Bridge`](crate::Bridge)).
20 fn purview(&self) -> AppHandle<App>;
21
22 /// The installed bridge's [`AppHandle`], or `None` if none was installed.
23 fn try_purview(&self) -> Option<AppHandle<App>>;
24
25 /// The address the MCP server is listening on — the port the OS picked,
26 /// when the requested one was `0`.
27 ///
28 /// `None` if no bridge was installed, or if it serves over stdio (which has
29 /// no address). Use it to reach the address from a view or a handler, long
30 /// after setup — to render it, or to put "copy the connect command" behind
31 /// a button.
32 ///
33 /// The `Option` is honest here in a way it would not be at the setup site:
34 /// an `App` may carry either transport, so this caller genuinely cannot
35 /// know. Where the transport *is* known the address comes without one,
36 /// from [`BoundBridge::local_addr`](crate::BoundBridge::local_addr).
37 fn purview_addr(&self) -> Option<SocketAddr>;
38}
39
40impl PurviewAppExt for App {
41 fn purview(&self) -> AppHandle<App> {
42 self.global::<Installed>().app.clone()
43 }
44
45 fn try_purview(&self) -> Option<AppHandle<App>> {
46 self.try_global::<Installed>().map(|i| i.app.clone())
47 }
48
49 fn purview_addr(&self) -> Option<SocketAddr> {
50 self.try_global::<Installed>().and_then(|i| i.addr)
51 }
52}