+ {words.wants(bot.name)} {app}
+
+ }
+ >
+ {why && (
+
+ {words.because(bot.name)} {why}
+
+ )}
+ {path && (
+
+ {path}
+
+ )}
+ {words.means(bot.name)}
+
+
+
+
+
+
+ );
+}
diff --git a/app/src/features/desktop/DesktopGrants.tsx b/app/src/features/desktop/DesktopGrants.tsx
new file mode 100644
index 00000000..0de2b18d
--- /dev/null
+++ b/app/src/features/desktop/DesktopGrants.tsx
@@ -0,0 +1,91 @@
+// What the bot may see and use on the owner's desktop (spec 24.10), in its
+// details: each app the owner let it see, with a button that takes it back.
+
+import { Monitor, X } from "lucide-react";
+import { useEffect, useState } from "react";
+import { useT } from "../../i18n";
+import type { Bot, DesktopGrant } from "../../lib/protocol.gen";
+import { useApi } from "../../store/context";
+import { Button } from "../../ui/Button";
+import { attempt } from "../../ui/toast";
+
+/** The bot's grants, read when shown and kept current by `bot.desktop`. */
+function useDesktopGrants(botId: Bot["id"]): DesktopGrant[] | null {
+ const api = useApi();
+ const [grants, setGrants] = useState