mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-05-25 18:55:19 +02:00
tools row in home
This commit is contained in:
parent
84aa980894
commit
8aa0c1763a
1 changed files with 125 additions and 2 deletions
|
|
@ -1,6 +1,7 @@
|
|||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { ArrowRight, Bot, Calendar, Clock, FileText, Mail, MessageSquare, Mic, Plus, Video } from 'lucide-react'
|
||||
import { ArrowRight, Bot, Calendar, Clock, FileText, Mail, MessageSquare, Mic, Plug, Plus, Video } from 'lucide-react'
|
||||
import { extractConferenceLink } from '@/lib/calendar-event'
|
||||
import { SettingsDialog } from '@/components/settings-dialog'
|
||||
|
||||
interface TreeNode {
|
||||
path: string
|
||||
|
|
@ -53,6 +54,7 @@ type RawCalEvent = {
|
|||
}
|
||||
|
||||
type EmailThread = { threadId: string; subject: string; from: string }
|
||||
type ToolkitPreview = { slug: string; logo: string; name: string; description: string }
|
||||
|
||||
function greeting(): string {
|
||||
const h = new Date().getHours()
|
||||
|
|
@ -152,6 +154,54 @@ 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 (
|
||||
<img
|
||||
src={toolkit.logo}
|
||||
alt=""
|
||||
className="hidden"
|
||||
onLoad={(event) => {
|
||||
const img = event.currentTarget
|
||||
if (img.naturalWidth > 1 && img.naturalHeight > 1) {
|
||||
setLoaded(true)
|
||||
} else {
|
||||
onInvalid(toolkit.slug)
|
||||
}
|
||||
}}
|
||||
onError={() => onInvalid(toolkit.slug)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
title={`${toolkit.name}: ${toolkit.description}`}
|
||||
aria-label={toolkit.name}
|
||||
className="flex size-6 shrink-0 items-center justify-center rounded-md border border-border bg-muted/60"
|
||||
>
|
||||
<img
|
||||
src={toolkit.logo}
|
||||
alt=""
|
||||
className="size-5 shrink-0 object-contain"
|
||||
onError={() => onInvalid(toolkit.slug)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function HomeView({
|
||||
tree,
|
||||
|
|
@ -168,6 +218,9 @@ export function HomeView({
|
|||
}: HomeViewProps) {
|
||||
const [events, setEvents] = useState<CalEvent[]>([])
|
||||
const [emails, setEmails] = useState<EmailThread[]>([])
|
||||
const [toolkitPreviews, setToolkitPreviews] = useState<ToolkitPreview[]>(cachedToolkitPreviews ?? [])
|
||||
const [toolkitLogosLoaded, setToolkitLogosLoaded] = useState(cachedToolkitLogosLoaded)
|
||||
const [connectionsSettingsOpen, setConnectionsSettingsOpen] = useState(false)
|
||||
|
||||
const loadEvents = useCallback(async () => {
|
||||
try {
|
||||
|
|
@ -207,7 +260,40 @@ export function HomeView({
|
|||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => { void loadEvents(); void loadEmails() }, [loadEvents, loadEmails])
|
||||
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 loadConnectorLogos() }, [loadEvents, loadEmails, loadConnectorLogos])
|
||||
|
||||
// Upcoming (not-yet-ended) events, soonest first.
|
||||
const upcoming = useMemo(() => {
|
||||
|
|
@ -437,6 +523,43 @@ export function HomeView({
|
|||
</div>
|
||||
)}
|
||||
|
||||
{/* Tool connections */}
|
||||
<div className={CARD}>
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="flex size-8 shrink-0 items-center justify-center rounded-lg border border-border bg-muted text-muted-foreground">
|
||||
<Plug className="size-[14px]" />
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="text-[13.5px] leading-snug">
|
||||
<span className="font-medium">Connect your tools.</span>
|
||||
<span className="text-muted-foreground"> Bring context from the apps you already use.</span>
|
||||
</div>
|
||||
<div className="mt-3 flex min-h-5 flex-wrap items-center gap-1.5">
|
||||
{toolkitLogosLoaded && toolkitPreviews.map((toolkit) => (
|
||||
<ToolkitPreviewIcon
|
||||
key={toolkit.slug}
|
||||
toolkit={toolkit}
|
||||
onInvalid={removeToolkitPreview}
|
||||
/>
|
||||
))}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setConnectionsSettingsOpen(true)}
|
||||
className="ml-1 flex h-5 shrink-0 items-center gap-1 rounded-md px-1 text-[12px] font-medium text-primary hover:underline"
|
||||
>
|
||||
Connections
|
||||
<ArrowRight className="size-3" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<SettingsDialog
|
||||
defaultTab="connections"
|
||||
open={connectionsSettingsOpen}
|
||||
onOpenChange={setConnectionsSettingsOpen}
|
||||
/>
|
||||
|
||||
{/* Open chat CTA */}
|
||||
{onOpenChat && (
|
||||
<button
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue