import { useCallback, useDeferredValue, useEffect, useMemo, useState } from 'react'; import { Link } from '@tanstack/react-router'; import { motion } from 'motion/react'; import { parseAsString, parseAsStringEnum, useQueryState } from 'nuqs'; import { ExternalLink, Package, PackagePlus, RefreshCw, Search, Trash2, X } from 'lucide-react'; import { Badge } from '../components/ui/badge'; import { Button, buttonVariants } from '../components/ui/button'; import { Tooltip, TooltipContent, TooltipTrigger } from '../components/ui/tooltip'; import { Card, CardAction, CardContent, CardDescription, CardHeader, CardTitle, } from '../components/ui/card'; import { Input } from '../components/ui/input'; import { AgentBadgeRow } from './agent-badge-row'; import { INSTALL_DIALOG_EVENT } from './constants'; import { PageLoadingState, StatusBanner } from './components'; import { useDashboardActions, useDashboardData } from './data'; import { InstallSkillDialog } from './install-skill-dialog'; import { SkillActionButton } from './skill-action-button'; import { useSkillActions } from './skill-actions'; import type { BrowserSkill, ScopeFilter } from './types'; import { createCommandFailureMessage, getErrorMessage, scopeLabel } from './utils'; export function BrowsePage() { const { payload, skills, isInitialLoading, errorMessage } = useDashboardData(); const { reload, refresh } = useDashboardActions(); const { removeSkill, updateSkill } = useSkillActions(); const [pendingActionKey, setPendingActionKey] = useState(null); const [search, setSearch] = useQueryState('search', parseAsString.withDefault('')); const [scopeFilter] = useQueryState( 'scope', parseAsStringEnum(['all', 'project', 'global']).withDefault('all') ); const [installParam, setInstallParam] = useQueryState( 'install', parseAsString.withOptions({ history: 'push' }) ); const [, setPreviewId] = useQueryState('preview'); const deferredSearch = useDeferredValue(search); const visibleSkills = useMemo(() => { const normalizedSearch = deferredSearch.trim().toLowerCase(); return skills.filter((skill) => { if (scopeFilter !== 'all' && skill.scope !== scopeFilter) { return false; } return normalizedSearch.length === 0 || skill.searchableText.includes(normalizedSearch); }); }, [deferredSearch, scopeFilter, skills]); const totalInstalled = payload ? payload.installedState.project.skills.length + payload.installedState.global.skills.length : 0; const isInstallDialogOpen = installParam === '1'; const openInstallDialog = useCallback(() => { void setInstallParam('1'); }, [setInstallParam]); const closeInstallDialog = useCallback(() => { void setInstallParam(null); void setPreviewId(null); }, [setInstallParam, setPreviewId]); const handleInstallDialogOpenChange = useCallback( (open: boolean) => { if (open) { openInstallDialog(); return; } closeInstallDialog(); }, [closeInstallDialog, openInstallDialog] ); useEffect(() => { const handleOpenInstallDialog = () => openInstallDialog(); window.addEventListener(INSTALL_DIALOG_EVENT, handleOpenInstallDialog); return () => { window.removeEventListener(INSTALL_DIALOG_EVENT, handleOpenInstallDialog); }; }, [openInstallDialog]); useEffect(() => { const handleKeyDown = (event: KeyboardEvent) => { if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === 'k') { event.preventDefault(); openInstallDialog(); } }; window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown); }, [openInstallDialog]); const handleRemoveSkill = async (skill: BrowserSkill) => { try { const outcome = await removeSkill(skill, { applyPayloadDelayMs: 500 }); if (outcome.command.ok) { return { ok: true }; } const message = createCommandFailureMessage(outcome.command); return { ok: false, errorMessage: message }; } catch (error) { const message = getErrorMessage(error); return { ok: false, errorMessage: message }; } }; const handleUpdateSkill = async (skill: BrowserSkill) => { try { const outcome = await updateSkill(skill); if (outcome.command.ok) { return { ok: true }; } const message = createCommandFailureMessage(outcome.command); return { ok: false, errorMessage: message }; } catch (error) { const message = getErrorMessage(error); return { ok: false, errorMessage: message }; } }; if (isInitialLoading && !payload) { return (
); } if (!payload) { return ( Unable to load skills {errorMessage ?? 'No data is available yet.'} ); } return (
{totalInstalled} installed skill{totalInstalled === 1 ? '' : 's'}
{errorMessage ? ( } message={errorMessage} /> ) : null} {visibleSkills.length === 0 ? ( No matching skills Adjust your filters or clear the search query. ) : ( {visibleSkills.map((skill) => (
{skill.name}

{skill.primarySource}

{scopeLabel(skill.scope)} {skill.sourceType ? ( {skill.sourceType} ) : null} {skill.ref ? ref: {skill.ref} : null}
} loadingLabel={`Updating ${skill.name}`} onPendingChange={setPendingActionKey} onAction={() => handleUpdateSkill(skill)} tooltip={skill.managed ? 'Update' : 'Local skills cannot be updated'} /> } loadingLabel={`Removing ${skill.name}`} onPendingChange={setPendingActionKey} confirmation={{ title: 'Remove skill?', description: `Remove "${skill.name}" from ${scopeLabel(skill.scope)}?`, actionLabel: 'Remove', }} onAction={() => handleRemoveSkill(skill)} tooltip="Remove" variant="ghost" /> Details

{skill.description}

Agents

{skill.agents.length > 0 ? ( ) : (
No agents declared
)}
))}
)}
); }