diff --git a/apps/web/components/dashboard-view.tsx b/apps/web/components/dashboard-view.tsx index b7c10a9e..37582908 100644 --- a/apps/web/components/dashboard-view.tsx +++ b/apps/web/components/dashboard-view.tsx @@ -9,7 +9,9 @@ import { useQuery } from "@tanstack/react-query" import { useRouter } from "next/navigation" import Image from "next/image" import { + Activity, ArrowRight, + Clock, ExternalLink, FileText, Lightbulb, @@ -298,32 +300,37 @@ const PLUGIN_STATIC = [ // Plugin catalog for tool usage display - maps plugin IDs to display names and icons const PLUGIN_DISPLAY_CATALOG: Record< string, - { name: string; icon: string; type: "Plugin" } + { name: string; icon: string; type: "Plugin"; description: string } > = { claude_code: { name: "Claude Code", icon: "/images/plugins/claude-code.svg", type: "Plugin", + description: "Persistent memory across coding sessions", }, opencode: { name: "OpenCode", icon: "/images/plugins/opencode.svg", type: "Plugin", + description: "Context that carries forward automatically", }, openclaw: { name: "OpenClaw", icon: "/images/plugins/openclaw.svg", type: "Plugin", + description: "Cross-channel memory for messaging", }, hermes: { name: "Hermes", icon: "/images/plugins/hermes.svg", type: "Plugin", + description: "Persistent conversation memory", }, codex: { name: "OpenAI Codex", icon: "/images/plugins/codex.svg", type: "Plugin", + description: "AI-powered code generation with memory", }, } @@ -335,6 +342,8 @@ interface ToolUsageItem { icon: string | null lastUsedAt: Date | null hasBeenUsed: boolean + connectedAt: Date | null + description: string | null } type ToolUsageApiKey = { @@ -395,6 +404,8 @@ function parseToolUsage( icon: catalog?.icon ?? null, lastUsedAt: lastUsed, hasBeenUsed: !!key.lastRequest, + connectedAt: toValidDate(key.createdAt), + description: catalog?.description ?? null, }) } } @@ -420,6 +431,8 @@ function parseToolUsage( icon: null, lastUsedAt: lastUsed, hasBeenUsed: !!key.lastRequest, + connectedAt: toValidDate(key.createdAt), + description: "Query your saved knowledge from any AI client", }) } } @@ -467,6 +480,8 @@ function parseToolUsage( icon: null, lastUsedAt: createdAt, hasBeenUsed: true, + connectedAt: toolMap.get("mcp")?.connectedAt ?? null, + description: "Query your saved knowledge from any AI client", }) } } @@ -500,15 +515,35 @@ function formatToolUsageTime(date: Date | null, hasBeenUsed: boolean): string { if (!hasBeenUsed) return "Never used" if (!date) return "Connected" const diffMs = Date.now() - date.getTime() + const diffMins = Math.floor(diffMs / (1000 * 60)) const diffHours = Math.floor(diffMs / (1000 * 60 * 60)) const diffDays = Math.floor(diffHours / 24) - if (diffHours < 1) return "Just now" + if (diffMins < 1) return "Just now" + if (diffMins < 60) return `${diffMins}m ago` if (diffHours < 24) return `${diffHours}h ago` if (diffDays === 1) return "Yesterday" if (diffDays < 7) return `${diffDays}d ago` return date.toLocaleDateString() } +// Format absolute time for tooltip display +function formatAbsoluteTime(date: Date | null): string { + if (!date) return "" + return date.toLocaleString(undefined, { + month: "short", + day: "numeric", + hour: "numeric", + minute: "2-digit", + hour12: true, + }) +} + +// Check if item was active recently (within last hour) +function isRecentlyActive(date: Date | null): boolean { + if (!date) return false + return Date.now() - date.getTime() < 60 * 60 * 1000 +} + function RecentToolUsageCard({ items, onOpenPlugins, @@ -548,66 +583,155 @@ function RecentToolUsageCard({ return (
-
)