fleet-memory/hindsight-docs/src/components/CookbookGrid.tsx
Ben ef90842f87
feat: hindsight-hermes integration for Hermes Agent (#600)
* feat: add hindsight-hermes integration for Hermes Agent

* chore: add Hermes docs page, icon, and release process bindings

- Add cookbook page for Hermes integration (synced with README)
- Add Hermes icon and map hindsight-hermes SDK tag in CookbookGrid
- Add cookbook entry to index.mdx
- Add hindsight-hermes to release.sh PYTHON_PACKAGES array
- Add build, publish, artifact upload, and release asset steps in release.yml

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 18:06:55 +01:00

130 lines
4.3 KiB
TypeScript

import React, {useState} from 'react';
import Link from '@docusaurus/Link';
import useBaseUrl from '@docusaurus/useBaseUrl';
import styles from './CookbookGrid.module.css';
export interface CookbookCard {
title: string;
href: string;
description?: string;
tags?: {
sdk?: string;
topic?: string;
};
}
interface CookbookGridProps {
items: CookbookCard[];
}
function sdkIcon(sdk: string): string | null {
if (sdk.startsWith('@') || sdk.includes('node') || sdk.includes('chat') || sdk.includes('ai-sdk')) {
return '/img/icons/nodejs.png';
}
if (sdk.includes('-go') || sdk === 'go') {
return '/img/icons/golang.png';
}
if (sdk.includes('hermes')) {
return '/img/icons/hermes.png';
}
if (sdk.includes('hindsight-client') || sdk.includes('hindsight-api') || sdk.includes('litellm') || sdk.includes('pydantic') || sdk.includes('crewai')) {
return '/img/icons/python.svg';
}
return null;
}
function SdkIcon({sdk, className}: {sdk: string; className?: string}) {
const icon = sdkIcon(sdk);
const src = useBaseUrl(icon ?? '');
if (!icon) return null;
return <img src={src} alt="" className={className} aria-hidden />;
}
function Card({title, href, description, tags}: CookbookCard) {
return (
<Link to={href} className={styles.card}>
<div className={styles.cardBody}>
<h3 className={styles.cardTitle}>{title}</h3>
{description && <p className={styles.cardDescription}>{description}</p>}
{(tags?.topic || tags?.sdk) && (
<div className={styles.cardFooter}>
{tags.topic && <span className={styles.cardTopic}>{tags.topic}</span>}
{tags.sdk && (
<span className={styles.cardSdk}>
<SdkIcon sdk={tags.sdk} className={styles.sdkIcon} />
{tags.sdk}
</span>
)}
</div>
)}
</div>
</Link>
);
}
export default function CookbookGrid({items}: CookbookGridProps) {
const [selectedTopic, setSelectedTopic] = useState<string | null>(null);
const [selectedSdk, setSelectedSdk] = useState<string | null>(null);
const topics = [...new Set(items.map((i) => i.tags?.topic).filter(Boolean))] as string[];
const sdks = [...new Set(items.map((i) => i.tags?.sdk).filter(Boolean))] as string[];
const filtered = items.filter((item) => {
if (selectedTopic && item.tags?.topic !== selectedTopic) return false;
if (selectedSdk && item.tags?.sdk !== selectedSdk) return false;
return true;
});
const hasFilters = topics.length > 1 || sdks.length > 1;
return (
<div>
{hasFilters && (
<div className={styles.filters}>
{topics.length > 1 && (
<div className={styles.filterGroup}>
<span className={styles.filterLabel}>Topic</span>
<button
className={`${styles.filterPill} ${selectedTopic === null ? styles.filterPillActive : ''}`}
onClick={() => setSelectedTopic(null)}>
All
</button>
{topics.map((topic) => (
<button
key={topic}
className={`${styles.filterPill} ${selectedTopic === topic ? styles.filterPillActive : ''}`}
onClick={() => setSelectedTopic(selectedTopic === topic ? null : topic)}>
{topic}
</button>
))}
</div>
)}
{sdks.length > 1 && (
<div className={styles.filterGroup}>
<span className={styles.filterLabel}>SDK</span>
<button
className={`${styles.filterPill} ${selectedSdk === null ? styles.filterPillActive : ''}`}
onClick={() => setSelectedSdk(null)}>
All
</button>
{sdks.map((sdk) => (
<button
key={sdk}
className={`${styles.filterPill} ${selectedSdk === sdk ? styles.filterPillActive : ''}`}
onClick={() => setSelectedSdk(selectedSdk === sdk ? null : sdk)}>
<SdkIcon sdk={sdk} className={styles.filterPillIcon} />
{sdk}
</button>
))}
</div>
)}
</div>
)}
<div className={styles.grid}>
{filtered.map((item) => (
<Card key={item.href} {...item} />
))}
</div>
</div>
);
}