diff --git a/docs/community/Streams/12-2024/2024-12-10.md b/docs/community/Streams/12-2024/2024-12-10.md index af7249ab2f0..51afc2133f2 100644 --- a/docs/community/Streams/12-2024/2024-12-10.md +++ b/docs/community/Streams/12-2024/2024-12-10.md @@ -15,45 +15,26 @@ YouTube Link: https://www.youtube.com/watch?v=6I9e9pJprDI Part 1: Trusted Execution Environments (TEEs) with Agent Joshua - **00:00:09** - Stream starts, initial setup issues. - - Link: - **00:01:58** - Intro to Trusted Execution Environments (TEEs). - - Link: - **00:08:03** - Agent Joshua begins explaining TEEs and the Eliza plugin. - - Link: - **00:19:15** - Deeper dive into remote attestation. - - Link: - **00:24:50** - Discussion of derived keys. - - Link: - **00:37:00** - Deploying to a real TEE, Phala Network's TEE cloud. - - Link: - **00:50:48** - Q&A with Joshua, contact info, and next steps. - - Link: Part 2: Building a Domino's pizza ordering agent - **01:04:37** - Transition to building a Domino's pizza ordering agent. - - Link: - **01:14:20** - Discussion of the pizza ordering agent’s order flow and state machine. - - Link: - **01:22:07** - Using Claude to generate a state machine diagram. - - Link: - **01:32:17** - Creating the Domino's plugin in Eliza. - - Link: - **01:54:15** - Working on the pizza order provider. - - Link: - **02:16:46** - Pizza provider code completed. - - Link: - **02:28:50** - Discussion of caching customer and order data. - - Link: - **03:13:45** - Pushing fixes to main branch and continuing work on the agent. - - Link: - **04:24:30** - Discussion of summarizing past agent dev school sessions. - - Link: - **05:01:18** - Shaw returns, admits to ordering Domino's manually. - - Link: - **05:09:00** - Discussing payment flow and a confirm order action. - - Link: - **05:27:17** - Final code push, wrap-up, and end of stream. - - Link: ## Summary diff --git a/docs/community/components/Accordion.tsx b/docs/community/components/Accordion.tsx new file mode 100644 index 00000000000..9b4748089ed --- /dev/null +++ b/docs/community/components/Accordion.tsx @@ -0,0 +1,183 @@ +import React, { useState, useRef, useEffect } from "react"; +import { GitHubItem } from "./Contributions"; +import { GITHUB_PAGE_LIMIT } from "./Contributors"; + +interface AccordionProps { + title: string; + isOpen: boolean; + onToggle: () => void; + data: GitHubItem[]; + loadMore?: () => void; + total_count: number; + primaryText?: string; + secondaryText?: string; + mainBackgroundColor?: string; +} + +export const Accordion: React.FC = ({ + title, + isOpen, + onToggle, + data, + loadMore, + total_count, + primaryText, + secondaryText, + mainBackgroundColor, +}) => { + const [hoveredIndex, setHoveredIndex] = useState(null); + const [hoverLoadMore, setHoverLoadMore] = useState(false); + const [maxHeight, setMaxHeight] = useState( + isOpen ? "1000px" : "0px", + ); + + const contentRef = useRef(null); + + React.useEffect(() => { + setMaxHeight(isOpen ? "1000px" : "0px"); + }, [isOpen]); + + useEffect(() => { + if (contentRef.current && data.length > GITHUB_PAGE_LIMIT) { + contentRef.current.scrollTo({ + top: contentRef.current.scrollHeight, + behavior: "smooth", + }); + } + }, [data]); + + return ( +
+
+
{title}
+
+ {"▶"} +
+
+
+
+ {data.map((entry, index) => ( +
+
setHoveredIndex(index)} + onMouseLeave={() => setHoveredIndex(null)} + onClick={() => + window.open( + entry.html_url, + "_blank", + "noopener,noreferrer", + ) + } + > +
+ {entry.bullet && ( +
+ )} +
{entry.title}
+
+
+ {entry.created_at.split("T")[0]} +
+
+
+ ))} +
+
+ {isOpen && loadMore && data.length < total_count && ( +
+ setHoverLoadMore(true)} + onMouseLeave={() => setHoverLoadMore(false)} + onClick={loadMore} + > + Load more + +
+ )} +
+ ); +}; diff --git a/docs/community/components/Contributions.tsx b/docs/community/components/Contributions.tsx new file mode 100644 index 00000000000..39fed8bbbe2 --- /dev/null +++ b/docs/community/components/Contributions.tsx @@ -0,0 +1,363 @@ +import React, { useState, useEffect } from "react"; +import { Accordion } from "./Accordion"; +import { StatCard } from "./StatCard"; +import { THEME_COLORS } from "./Contributors"; +import { hexToRgb, useGithubAccessToken } from "./utils"; +import ScoreIcon from "./ScoreIcon"; +import Summary from "./Summary"; +import Hero from "./Hero"; + +export interface GitHubItem { + html_url: string; + title: string; + created_at: string; + bullet?: string; +} + +export interface StatCardProps { + title: string; + value: number; + style?: React.CSSProperties; +} + +export interface AccordionItem { + data: GitHubItem[]; + total_count: number; + state?: string; +} + +export enum BULLET_COLOR { + OPEN = "#1A7F37", + CLOSE = "#D1242F", + MERGE = "#8250DF", +} + +const initializeAccordionItem = (): AccordionItem => ({ + data: [], + total_count: 0, +}); + +const Contributions = ({ + contributor, + onBack, + darkMode, + activitySummary, + score, +}) => { + const githubAccessToken = useGithubAccessToken(); + const [commitsData, setCommitsData] = useState( + initializeAccordionItem(), + ); + const [prsData, setPrsData] = useState( + initializeAccordionItem(), + ); + const [issuesData, setIssuesData] = useState( + initializeAccordionItem(), + ); + const [openAccordion, setOpenAccordion] = useState(null); + + const [commitPage, setCommitPage] = useState(1); + const [prPage, sePrPage] = useState(1); + const [issuePage, setIssuePage] = useState(1); + + useEffect(() => { + const fetchContributorStats = async () => { + try { + await fetchCommits(commitPage); + await fetchPRs(prPage); + await fetchIssues(issuePage); + } catch (error) { + console.error("Error fetching contributor stats:", error); + } + }; + + fetchContributorStats(); + }, [contributor.login]); + + const toggleAccordion = (section: string) => { + setOpenAccordion((prev) => (prev === section ? null : section)); + }; + + const fetchCommits = async (page: number) => { + try { + const commitResponse = await fetch( + `https://api.github.com/repos/ai16z/eliza/commits?author=${contributor.login}&page=${page}`, + { + method: "GET", + headers: { + Authorization: `token ${githubAccessToken}`, + Accept: "application/vnd.github.v3+json", + }, + }, + ); + const commitData = await commitResponse.json(); + const commitItems = commitData.map((commit: any) => ({ + html_url: commit.html_url, + title: commit.commit.message, + created_at: commit.commit.author.date, + })); + const currentCommitsData = [...commitsData.data, ...commitItems]; + setCommitsData({ + data: currentCommitsData, + total_count: contributor.contributions, + }); + } catch (error) { + console.error("Error fetching commits:", error); + } + }; + + const fetchPRs = async (page: number) => { + try { + const prResponse = await fetch( + `https://api.github.com/search/issues?q=type:pr+author:${contributor.login}+repo:ai16z/eliza&page=${page}`, + { + method: "GET", + headers: { + Authorization: `token ${githubAccessToken}`, + Accept: "application/vnd.github.v3+json", + }, + }, + ); + const prData = await prResponse.json(); + const prItems = prData.items.map((pr: any) => ({ + html_url: pr.html_url, + title: pr.title, + created_at: pr.created_at, + bullet: + pr.state === "open" + ? BULLET_COLOR.OPEN + : pr.pull_request.merged_at + ? BULLET_COLOR.MERGE + : BULLET_COLOR.CLOSE, + })); + const currentPrsData = [...prsData.data, ...prItems]; + + setPrsData({ + data: currentPrsData, + total_count: prData.total_count, + }); + } catch (error) { + console.error("Error fetching PRs:", error); + } + }; + + const fetchIssues = async (page: number) => { + try { + const issueResponse = await fetch( + `https://api.github.com/search/issues?q=type:issue+author:${contributor.login}+repo:ai16z/eliza&page=${page}`, + { + method: "GET", + headers: { + Authorization: `token ${githubAccessToken}`, + Accept: "application/vnd.github.v3+json", + }, + }, + ); + const issueData = await issueResponse.json(); + const issueItems = issueData.items.map((issue: any) => ({ + html_url: issue.html_url, + title: issue.title, + created_at: issue.created_at, + bullet: + issue.state === "open" + ? BULLET_COLOR.OPEN + : BULLET_COLOR.CLOSE, + })); + const currentIssuesData = [...issuesData.data, ...issueItems]; + setIssuesData({ + data: currentIssuesData, + total_count: issueData.total_count, + }); + } catch (error) { + console.error("Error fetching issues:", error); + } + }; + + const accordionItems = [ + { + title: "Commits", + data: commitsData, + section: "commits", + loadMore: () => { + const nextPage = commitPage + 1; + fetchCommits(nextPage); + setCommitPage(nextPage); + }, + }, + { + title: "Pull Requests", + data: prsData, + section: "pullRequests", + loadMore: () => { + const nextPage = prPage + 1; + fetchPRs(nextPage); + sePrPage(nextPage); + }, + }, + { + title: "Issues", + data: issuesData, + section: "issues", + loadMore: () => { + const nextPage = issuePage + 1; + fetchIssues(nextPage); + setIssuePage(nextPage); + }, + }, + ]; + + return ( +
+
+ + + back + +
+
+ + +
+ + + +
+ {accordionItems.map((stat, index) => ( + + ))} +
+
+ {accordionItems.map((item) => ( + toggleAccordion(item.section)} + data={item.data.data} + loadMore={item.loadMore} + total_count={item.data.total_count} + primaryText={ + darkMode + ? THEME_COLORS.dark.primaryText + : THEME_COLORS.light.primaryText + } + secondaryText={ + darkMode + ? THEME_COLORS.dark.secondaryText + : THEME_COLORS.light.secondaryText + } + mainBackgroundColor={ + darkMode + ? THEME_COLORS.dark.mainBackgroundColor + : THEME_COLORS.light.mainBackgroundColor + } + /> + ))} +
+
+ ); +}; + +export default Contributions; diff --git a/docs/community/components/Contributor.tsx b/docs/community/components/Contributor.tsx new file mode 100644 index 00000000000..45efcb551e4 --- /dev/null +++ b/docs/community/components/Contributor.tsx @@ -0,0 +1,96 @@ +import React, { useState } from "react"; +import { ContributorProps } from "./Contributors"; +import { THEME_COLORS } from "./Contributors"; +import { hexToRgb } from "./utils"; +import ScoreIcon from "./ScoreIcon"; +import Summary from "./Summary"; +import Hero from "./Hero"; + +const ContributorCard: React.FC = ({ + contributor, + onSelect, + darkMode, + activitySummary, + score, +}) => { + const [isHovered, setIsHovered] = useState(false); + + return ( +
setIsHovered(true)} + onMouseLeave={() => setIsHovered(false)} + onClick={onSelect} + > + + + +
+ ); +}; + +export default ContributorCard; diff --git a/docs/community/components/Contributors.tsx b/docs/community/components/Contributors.tsx new file mode 100644 index 00000000000..ecbd730f684 --- /dev/null +++ b/docs/community/components/Contributors.tsx @@ -0,0 +1,233 @@ +import React, { useEffect, useState, useRef } from "react"; +import ContributorCard from "./Contributor"; +import Contributions from "./Contributions"; +import { useColorMode } from "@docusaurus/theme-common"; +import contributorsSpec from "../contributors.json"; +import { useGithubAccessToken } from "./utils"; + +export interface Contributor { + id: number; + login: string; + avatar_url: string; + html_url: string; + contributions: number; +} + +export interface ContributorProps { + contributor: Contributor; + onSelect: () => void; + darkMode: boolean; + activitySummary?: string; + score?: number; +} + +export const THEME_COLORS = { + light: { + mainBackgroundColor: "#ffffff", + secondaryBackground: "rgba(0, 0, 0, 0.05)", + primaryText: "#000000", + secondaryText: "#ffa600", + }, + dark: { + mainBackgroundColor: "#1b1b1d", + secondaryBackground: "#242526", + primaryText: "#ffffff", + secondaryText: "#add8e6", + }, +}; + +export interface ActivityDetails { + score: number; + activitySummary: string; +} + +export const GITHUB_PAGE_LIMIT = 30; // The maximum number to fetch per page from the GitHub API. + +const Contributors: React.FC = () => { + const githubAccessToken = useGithubAccessToken(); + const { colorMode } = useColorMode(); + const [selectedContributor, setSelectedContributor] = + useState(null); + const [contributors, setContributors] = useState([]); + const [error, setError] = useState(null); + const [darkMode, setDarkMode] = useState(colorMode === "dark"); + const [hasMore, setHasMore] = useState(true); + const [activitySummaries, setActivitySummaries] = useState< + Map + >(new Map()); + + const observerRef = useRef(null); + const pageRef = useRef(1); + const loadingRef = useRef(true); + + useEffect(() => { + setDarkMode(colorMode === "dark"); + }, [colorMode]); + + const fetchContributors = async (page: number) => { + loadingRef.current = true; + try { + const response = await fetch( + `https://api.github.com/repos/ai16z/eliza/contributors?per_page=${GITHUB_PAGE_LIMIT}&page=${page}`, + { + method: "GET", + headers: { + Authorization: `token ${githubAccessToken}`, + Accept: "application/vnd.github.v3+json", + }, + }, + ); + if (!response.ok) { + throw new Error( + `Error fetching contributors: ${response.statusText}`, + ); + } + const data: Contributor[] = await response.json(); + if (data.length === 0) { + setHasMore(false); + return; + } + const currentContributors = [...contributors, ...data]; + + setContributors(currentContributors); + } catch (err) { + setError(err instanceof Error ? err.message : "Unknown error"); + } finally { + loadingRef.current = false; + } + }; + + useEffect(() => { + const fetchActivitySummaries = async () => { + try { + const response = await fetch( + "https://ai16z.github.io/data/contributors.json", + ); + if (!response.ok) { + throw new Error( + `Error fetching activity summaries: ${response.statusText}`, + ); + } + const specs = await response.json(); + + const currentActivitySummaries = new Map< + string, + ActivityDetails + >(); + specs.forEach( + (spec: { + contributor: string; + score: number; + summary: string; + }) => { + currentActivitySummaries.set(spec.contributor, { + score: spec.score, + activitySummary: spec.summary, + }); + }, + ); + setActivitySummaries(currentActivitySummaries); + } catch (err) { + console.log("Unknown error while fetching summaries"); + } + }; + + fetchActivitySummaries(); + fetchContributors(pageRef.current); + }, []); + + useEffect(() => { + const observer = new IntersectionObserver( + (entries) => { + if ( + entries[0].isIntersecting && + !loadingRef.current && + hasMore + ) { + loadingRef.current = true; + pageRef.current++; + fetchContributors(pageRef.current); + } + }, + { threshold: 1.0 }, + ); + + if (observerRef.current) { + observer.observe(observerRef.current); + } + + return () => { + if (observerRef.current) { + observer.unobserve(observerRef.current); + } + }; + }, [contributors, hasMore, selectedContributor]); + + if (error) { + return
Error: {error}
; + } + + if (!contributors.length) { + return
Loading...
; + } + + return ( +
+ {selectedContributor ? ( + setSelectedContributor(null)} + darkMode={darkMode} + activitySummary={ + activitySummaries.get(selectedContributor.login) + ?.activitySummary + } + score={ + activitySummaries.get(selectedContributor.login)?.score + } + /> + ) : ( + <> + {contributors.map((contributor) => ( + { + setSelectedContributor(contributor); + }} + darkMode={darkMode} + activitySummary={ + activitySummaries.get(contributor.login) + ?.activitySummary + } + score={ + activitySummaries.get(contributor.login)?.score + } + /> + ))} +
+ {hasMore &&
Loading more...
} + + )} +
+ ); +}; + +export default Contributors; diff --git a/docs/community/components/Hero.tsx b/docs/community/components/Hero.tsx new file mode 100644 index 00000000000..4575fd4080f --- /dev/null +++ b/docs/community/components/Hero.tsx @@ -0,0 +1,47 @@ +import React from "react"; + +export default function Hero({ + contributor, + secondaryText, + profilePictureSize, +}) { + return ( +
+ {`${contributor.login}'s +
+
+ {contributor.login} +
+ { +
+ {contributor.contributions} contributions +
+ } +
+
+ ); +} diff --git a/docs/community/components/ScoreIcon.tsx b/docs/community/components/ScoreIcon.tsx new file mode 100644 index 00000000000..fcf5ae325ca --- /dev/null +++ b/docs/community/components/ScoreIcon.tsx @@ -0,0 +1,32 @@ +import React from "react"; + +export default function ScoreIcon({ style, iconColor, iconSize, score }) { + function Flash({ size, fill }) { + return ( + + + + ); + } + + return ( +
+ +
{typeof score === "number" ? score : "NULL"}
+
+ ); +} diff --git a/docs/community/components/StatCard.tsx b/docs/community/components/StatCard.tsx new file mode 100644 index 00000000000..d812de946fc --- /dev/null +++ b/docs/community/components/StatCard.tsx @@ -0,0 +1,22 @@ +import React from "react"; +import { StatCardProps } from "./Contributions"; + +export const StatCard: React.FC = ({ title, value, style }) => { + return ( +
+
{title}
+
{value}
+
+ ); +}; diff --git a/docs/community/components/Summary.tsx b/docs/community/components/Summary.tsx new file mode 100644 index 00000000000..b9a0a906360 --- /dev/null +++ b/docs/community/components/Summary.tsx @@ -0,0 +1,13 @@ +import React from "react"; + +export default function Summary({ summary, style }) { + return ( +
+ {summary || "No summary available"} +
+ ); +} diff --git a/docs/community/components/utils.tsx b/docs/community/components/utils.tsx new file mode 100644 index 00000000000..bd660f59591 --- /dev/null +++ b/docs/community/components/utils.tsx @@ -0,0 +1,15 @@ +import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; + +export function hexToRgb(hex: string) { + hex = hex.replace("#", ""); + const bigint = parseInt(hex, 16); + const r = (bigint >> 16) & 255; + const g = (bigint >> 8) & 255; + const b = bigint & 255; + return `${r}, ${g}, ${b}`; +} + +export function useGithubAccessToken() { + const { siteConfig } = useDocusaurusContext(); + return siteConfig.customFields.GITHUB_ACCESS_TOKEN; +} diff --git a/docs/community/profiles.mdx b/docs/community/profiles.mdx index 28224acecd9..5135aede388 100644 --- a/docs/community/profiles.mdx +++ b/docs/community/profiles.mdx @@ -1,17 +1,10 @@ --- -title: GitHub Contributors +title: GitHub Contributors description: GitHub contributors to our project --- -# GitHub Contributors +import Contributors from "./components/Contributors"; -This is a quick and dirty implementation of profiles that are programmatically generated from github data from `ai16z/eliza` repo. I'm looking for some help to integrate into Docusaurus as react components. See the code for generating profiles here: https://github.com/ai16z/ai16z.github.io +# GitHub Contributors -