From c8d801a123015485a78d1ba9b2a0bcf27011717b Mon Sep 17 00:00:00 2001
From: Arjun <6592213+arkml@users.noreply.github.com>
Date: Sat, 20 Jun 2026 03:02:41 +0530
Subject: [PATCH] add toolkits to the new chat for ease of discovery
---
.../src/components/chat-empty-state.tsx | 3 +
.../renderer/src/components/home-view.tsx | 127 +---------------
.../src/components/tool-connections-card.tsx | 138 ++++++++++++++++++
3 files changed, 145 insertions(+), 123 deletions(-)
create mode 100644 apps/x/apps/renderer/src/components/tool-connections-card.tsx
diff --git a/apps/x/apps/renderer/src/components/chat-empty-state.tsx b/apps/x/apps/renderer/src/components/chat-empty-state.tsx
index bb9cea8d..9423d060 100644
--- a/apps/x/apps/renderer/src/components/chat-empty-state.tsx
+++ b/apps/x/apps/renderer/src/components/chat-empty-state.tsx
@@ -2,6 +2,7 @@ import { ArrowUpRight, Bot, Mail, MessageSquare, Sparkles, Telescope } from 'luc
import { cn } from '@/lib/utils'
import { formatRelativeTime } from '@/lib/relative-time'
+import { ToolConnectionsCard } from '@/components/tool-connections-card'
export interface ChatEmptyStateRun {
id: string
@@ -101,6 +102,8 @@ export function ChatEmptyState({
))}
+
+
)
}
diff --git a/apps/x/apps/renderer/src/components/home-view.tsx b/apps/x/apps/renderer/src/components/home-view.tsx
index 1f1e6e6f..43b5978b 100644
--- a/apps/x/apps/renderer/src/components/home-view.tsx
+++ b/apps/x/apps/renderer/src/components/home-view.tsx
@@ -1,7 +1,7 @@
import { useCallback, useEffect, useMemo, useState } from 'react'
-import { ArrowRight, Bot, Calendar, Clock, ExternalLink, FileText, Mail, MessageSquare, Mic, Plug, Plus, Video } from 'lucide-react'
+import { ArrowRight, Bot, Calendar, Clock, ExternalLink, FileText, Mail, MessageSquare, Mic, Plus, Video } from 'lucide-react'
import { extractConferenceLink } from '@/lib/calendar-event'
-import { SettingsDialog } from '@/components/settings-dialog'
+import { ToolConnectionsCard } from '@/components/tool-connections-card'
interface TreeNode {
path: string
@@ -65,7 +65,6 @@ type SlackFeedMessage = {
ts: string
url?: string
}
-type ToolkitPreview = { slug: string; logo: string; name: string; description: string }
function greeting(): string {
const h = new Date().getHours()
@@ -187,54 +186,6 @@ function triggerMeetingCapture(event: CalEvent, openConference: boolean) {
}
const CARD = 'rounded-xl border border-border bg-card p-4'
-const TOOLKIT_PREVIEW_LIMIT = 8
-
-let cachedToolkitPreviews: ToolkitPreview[] | null = null
-let cachedToolkitLogosLoaded = false
-
-function ToolkitPreviewIcon({
- toolkit,
- onInvalid,
-}: {
- toolkit: ToolkitPreview
- onInvalid: (slug: string) => void
-}) {
- const [loaded, setLoaded] = useState(false)
-
- if (!loaded) {
- return (
-
{
- const img = event.currentTarget
- if (img.naturalWidth > 1 && img.naturalHeight > 1) {
- setLoaded(true)
- } else {
- onInvalid(toolkit.slug)
- }
- }}
- onError={() => onInvalid(toolkit.slug)}
- />
- )
- }
-
- return (
-
-

onInvalid(toolkit.slug)}
- />
-
- )
-}
export function HomeView({
tree,
@@ -255,9 +206,6 @@ export function HomeView({
const [slackMessages, setSlackMessages] = useState([])
const [slackError, setSlackError] = useState(null)
const [slackErrorKind, setSlackErrorKind] = useState(null)
- const [toolkitPreviews, setToolkitPreviews] = useState(cachedToolkitPreviews ?? [])
- const [toolkitLogosLoaded, setToolkitLogosLoaded] = useState(cachedToolkitLogosLoaded)
- const [connectionsSettingsOpen, setConnectionsSettingsOpen] = useState(false)
const loadEvents = useCallback(async () => {
try {
@@ -313,40 +261,7 @@ export function HomeView({
}
}, [])
- const loadConnectorLogos = useCallback(async () => {
- if (cachedToolkitLogosLoaded) return
- try {
- const configured = await window.ipc.invoke('composio:is-configured', null)
- if (!configured.configured) return
- const toolkits = await window.ipc.invoke('composio:list-toolkits', {})
- const previews = toolkits.items
- .filter((toolkit) => Boolean(toolkit.meta.logo))
- .slice(0, TOOLKIT_PREVIEW_LIMIT)
- .map((toolkit) => ({
- slug: toolkit.slug,
- logo: toolkit.meta.logo,
- name: toolkit.name,
- description: toolkit.meta.description,
- }))
- cachedToolkitPreviews = previews
- setToolkitPreviews(previews)
- } catch {
- cachedToolkitPreviews = []
- } finally {
- cachedToolkitLogosLoaded = true
- setToolkitLogosLoaded(true)
- }
- }, [])
-
- const removeToolkitPreview = useCallback((slug: string) => {
- setToolkitPreviews((prev) => {
- const next = prev.filter((toolkit) => toolkit.slug !== slug)
- cachedToolkitPreviews = next
- return next
- })
- }, [])
-
- useEffect(() => { void loadEvents(); void loadEmails(); void loadSlackMessages(); void loadConnectorLogos() }, [loadEvents, loadEmails, loadSlackMessages, loadConnectorLogos])
+ useEffect(() => { void loadEvents(); void loadEmails(); void loadSlackMessages() }, [loadEvents, loadEmails, loadSlackMessages])
// Upcoming (not-yet-ended) events, soonest first.
const upcoming = useMemo(() => {
@@ -624,41 +539,7 @@ export function HomeView({
)}
{/* Tool connections */}
-
-
-
-
-
- Connect your tools.
- Bring context from the apps you already use.
-
-
- {toolkitLogosLoaded && toolkitPreviews.map((toolkit) => (
-
- ))}
-
-
-
-
-
-
+
{/* Open chat CTA */}
{onOpenChat && (
diff --git a/apps/x/apps/renderer/src/components/tool-connections-card.tsx b/apps/x/apps/renderer/src/components/tool-connections-card.tsx
new file mode 100644
index 00000000..70c0d3fe
--- /dev/null
+++ b/apps/x/apps/renderer/src/components/tool-connections-card.tsx
@@ -0,0 +1,138 @@
+import { useCallback, useEffect, useState } from 'react'
+import { ArrowRight, Plug } from 'lucide-react'
+
+import { SettingsDialog } from '@/components/settings-dialog'
+import { cn } from '@/lib/utils'
+
+type ToolkitPreview = { slug: string; logo: string; name: string; description: string }
+
+const TOOLKIT_PREVIEW_LIMIT = 8
+
+let cachedToolkitPreviews: ToolkitPreview[] | null = null
+let cachedToolkitLogosLoaded = false
+
+function ToolkitPreviewIcon({
+ toolkit,
+ onInvalid,
+}: {
+ toolkit: ToolkitPreview
+ onInvalid: (slug: string) => void
+}) {
+ const [loaded, setLoaded] = useState(false)
+
+ if (!loaded) {
+ return (
+
{
+ const img = event.currentTarget
+ if (img.naturalWidth > 1 && img.naturalHeight > 1) {
+ setLoaded(true)
+ } else {
+ onInvalid(toolkit.slug)
+ }
+ }}
+ onError={() => onInvalid(toolkit.slug)}
+ />
+ )
+ }
+
+ return (
+
+

onInvalid(toolkit.slug)}
+ />
+
+ )
+}
+
+export function ToolConnectionsCard({ className }: { className?: string }) {
+ const [toolkitPreviews, setToolkitPreviews] = useState(cachedToolkitPreviews ?? [])
+ const [toolkitLogosLoaded, setToolkitLogosLoaded] = useState(cachedToolkitLogosLoaded)
+ const [connectionsSettingsOpen, setConnectionsSettingsOpen] = useState(false)
+
+ const loadConnectorLogos = useCallback(async () => {
+ if (cachedToolkitLogosLoaded) return
+ try {
+ const configured = await window.ipc.invoke('composio:is-configured', null)
+ if (!configured.configured) return
+ const toolkits = await window.ipc.invoke('composio:list-toolkits', {})
+ const previews = toolkits.items
+ .filter((toolkit) => Boolean(toolkit.meta.logo))
+ .slice(0, TOOLKIT_PREVIEW_LIMIT)
+ .map((toolkit) => ({
+ slug: toolkit.slug,
+ logo: toolkit.meta.logo,
+ name: toolkit.name,
+ description: toolkit.meta.description,
+ }))
+ cachedToolkitPreviews = previews
+ setToolkitPreviews(previews)
+ } catch {
+ cachedToolkitPreviews = []
+ } finally {
+ cachedToolkitLogosLoaded = true
+ setToolkitLogosLoaded(true)
+ }
+ }, [])
+
+ const removeToolkitPreview = useCallback((slug: string) => {
+ setToolkitPreviews((prev) => {
+ const next = prev.filter((toolkit) => toolkit.slug !== slug)
+ cachedToolkitPreviews = next
+ return next
+ })
+ }, [])
+
+ useEffect(() => {
+ void loadConnectorLogos()
+ }, [loadConnectorLogos])
+
+ return (
+ <>
+
+
+
+
+
+ Bring context from and take action in the apps you already use.
+
+
+ {toolkitLogosLoaded && toolkitPreviews.map((toolkit) => (
+
+ ))}
+
+
+
+
+
+
+ >
+ )
+}