feat: add download badge to uninstalled AI tools in tool grid

Show a download icon on AI tools that are not yet installed, and fetch
feature status on mount in both the sidebar tool panel and fullscreen
grid page.
This commit is contained in:
ashim-hq
2026-04-18 02:47:42 +08:00
parent 0b9c760e5e
commit fe1ae8436d
3 changed files with 20 additions and 1 deletions
+8 -1
View File
@@ -1,8 +1,10 @@
import type { Tool } from "@ashim/shared";
import { FileImage, Star } from "lucide-react";
import { PYTHON_SIDECAR_TOOLS } from "@ashim/shared";
import { Download, FileImage, Star } from "lucide-react";
import { Link } from "react-router-dom";
import { ICON_MAP } from "@/lib/icon-map";
import { cn } from "@/lib/utils";
import { useFeaturesStore } from "@/stores/features-store";
interface ToolCardProps {
tool: Tool;
@@ -12,6 +14,10 @@ export function ToolCard({ tool }: ToolCardProps) {
const IconComponent =
(ICON_MAP[tool.icon] as React.ComponentType<{ className?: string }>) ?? FileImage;
const isAiTool = (PYTHON_SIDECAR_TOOLS as readonly string[]).includes(tool.id);
const isInstalled = useFeaturesStore((s) => s.isToolInstalled)(tool.id);
const showDownloadBadge = isAiTool && !isInstalled;
return (
<div className="group flex items-center gap-3 relative">
<button
@@ -36,6 +42,7 @@ export function ToolCard({ tool }: ToolCardProps) {
Experimental
</span>
)}
{showDownloadBadge && <Download className="h-3.5 w-3.5 text-muted-foreground" />}
</Link>
</div>
);
@@ -1,5 +1,6 @@
import { CATEGORIES, TOOLS } from "@ashim/shared";
import { useEffect, useMemo, useState } from "react";
import { useFeaturesStore } from "@/stores/features-store";
import { useSettingsStore } from "@/stores/settings-store";
import { SearchBar } from "../common/search-bar";
import { ToolCard } from "../common/tool-card";
@@ -7,11 +8,16 @@ import { ToolCard } from "../common/tool-card";
export function ToolPanel() {
const [search, setSearch] = useState("");
const { disabledTools, experimentalEnabled, loaded, fetch } = useSettingsStore();
const fetchFeatures = useFeaturesStore((s) => s.fetch);
useEffect(() => {
fetch();
}, [fetch]);
useEffect(() => {
fetchFeatures();
}, [fetchFeatures]);
const visibleTools = useMemo(() => {
if (!loaded) return [];
return TOOLS.filter((t) => {
@@ -7,6 +7,7 @@ import { GemLogo } from "@/components/common/gem-logo";
import { apiGet } from "@/lib/api";
import { ICON_MAP } from "@/lib/icon-map";
import { cn } from "@/lib/utils";
import { useFeaturesStore } from "@/stores/features-store";
export function FullscreenGridPage() {
const [search, setSearch] = useState("");
@@ -14,6 +15,11 @@ export function FullscreenGridPage() {
const navigate = useNavigate();
const [disabledTools, setDisabledTools] = useState<string[]>([]);
const [experimentalEnabled, setExperimentalEnabled] = useState(false);
const fetchFeatures = useFeaturesStore((s) => s.fetch);
useEffect(() => {
fetchFeatures();
}, [fetchFeatures]);
useEffect(() => {
apiGet<{ settings: Record<string, string> }>("/v1/settings")