Skip to content

Commit

Permalink
feat: add initial components
Browse files Browse the repository at this point in the history
feat: add initial components
  • Loading branch information
0xAlec authored Oct 31, 2024
2 parents 6f5ab3c + a85f348 commit 10883de
Show file tree
Hide file tree
Showing 5 changed files with 255 additions and 131 deletions.
35 changes: 35 additions & 0 deletions agent-frontend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env*.local

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts

.env
153 changes: 153 additions & 0 deletions agent-frontend/app/components/AgentComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import { useState, useEffect, ChangeEvent, useCallback } from "react";
import TerminalCursor from "./TerminalCursor";

type ThoughtEntry = {
timestamp: Date;
content: string;
};

type AnimatedData = {
earned: number;
spent: number;
staked: number;
transactions: number;
thoughts: number;
};

export default function AgentComponent() {
const [currentTime, setCurrentTime] = useState(new Date());
const [thoughts, setThoughts] = useState<ThoughtEntry[]>([]);
const [userInput, setUserInput] = useState("");
const [animatedData, setAnimatedData] = useState<AnimatedData>({
earned: 10000,
spent: 4000,
staked: 1000,
transactions: 0,
thoughts: 900,
});

const generateRandomThought = useCallback((): ThoughtEntry => {
const thoughts = [
"Analyzing data patterns...",
"Processing natural language input...",
"Optimizing neural networks...",
"Generating creative solutions...",
"Evaluating ethical implications...",
"Simulating complex scenarios...",
"Integrating cross-domain knowledge...",
"Refining machine learning models...",
"Exploring innovative algorithms...",
"Synthesizing information from multiple sources...",
];
return {
timestamp: new Date(),
content: thoughts[Math.floor(Math.random() * thoughts.length)],
};
}, []);

const formatGMTDate = useCallback((date: Date) => {
return date.toISOString().replace("T", " ").slice(0, -5);
}, []);

useEffect(() => {
const timeInterval = setInterval(() => {
setCurrentTime(new Date());
}, 1000);

const thoughtInterval = setInterval(() => {
const newThought = generateRandomThought();
setThoughts((prevThoughts) => [...prevThoughts, newThought].slice(-10));
setAnimatedData((prev) => ({
...prev,
thoughts: prev.thoughts + 1,
}));
}, 3000);

const dataInterval = setInterval(() => {
setAnimatedData((prev) => ({
earned: prev.earned + Math.random() * 10,
spent: prev.spent + Math.random() * 5,
staked: prev.staked + Math.random() * 2,
transactions: prev.transactions + (Math.random() > 0.7 ? 1 : 0),
thoughts: prev.thoughts,
}));
}, 2000);

return () => {
clearInterval(timeInterval);
clearInterval(thoughtInterval);
clearInterval(dataInterval);
};
}, [generateRandomThought]);

const handleInputChange = useCallback(
(e: ChangeEvent<HTMLTextAreaElement>) => {
setUserInput(e.target.value);
},
[]
);

const handleSubmit = useCallback(
(e: React.FormEvent) => {
e.preventDefault();
console.log("User input:", userInput);
setUserInput("");
},
[userInput]
);

return (
<div className="flex flex-col h-screen bg-black font-mono ock-text-primary">
<div className="p-4 flex items-center justify-between border-b border-[#5788FA]">
<h1 className="text-xl font-bold">Based Agent</h1>
<div className="text-sm" aria-live="polite">
{formatGMTDate(currentTime)} GMT
</div>
</div>
<div className="flex flex-grow overflow-hidden">
<div className="flex-grow p-4 overflow-y-auto">
<p>Streaming real-time thoughts and actions...</p>
<div className="mt-4 space-y-2" role="log" aria-live="polite">
{thoughts.map((thought, index) => (
<div key={index} className="flex">
<span className="mr-2 ock-text-primary">
{formatGMTDate(thought.timestamp)}
</span>
<span>{thought.content}</span>
</div>
))}
</div>
<TerminalCursor />
</div>
<div className="w-1/3 p-4 border-l border-[#5788FA] flex flex-col">
<div className="mb-4 p-4 border border-[#5788FA]">
<ul className="space-y-1">
<li>Earned: ${animatedData.earned.toFixed(2)}</li>
<li>Spent: ${animatedData.spent.toFixed(2)}</li>
<li>Staked: ${animatedData.staked.toFixed(2)}</li>
<li>Transactions: {animatedData.transactions}</li>
<li>Thoughts: {animatedData.thoughts}</li>
<li>Friends: {animatedData.transactions}</li>
</ul>
</div>
<form onSubmit={handleSubmit} className="flex-grow flex flex-col">
<div className="relative flex-grow">
<textarea
value={userInput}
onChange={handleInputChange}
className="w-full h-full bg-black border border-[#5788FA] ock-text-primary p-2 pb-12 resize-none placeholder-[#5788FA] placeholder-opacity-50"
placeholder="Type your prompt here..."
/>
<button
type="submit"
className="absolute rounded bottom-2 right-2 ock-bg-primary text-black px-6 py-1.5 hover:bg-[#3D7BFF] transition-colors"
>
Send
</button>
</div>
</form>
</div>
</div>
</div>
);
}
64 changes: 64 additions & 0 deletions agent-frontend/app/components/TerminalCursor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { useState, useEffect, useRef, useCallback } from "react";

export default function TerminalCursor() {
const [text, setText] = useState("");
const [cursorVisible, setCursorVisible] = useState(true);
const inputRef = useRef<HTMLInputElement>(null);

useEffect(() => {
const cursorInterval = setInterval(() => {
setCursorVisible((prev) => !prev);
}, 530);

return () => clearInterval(cursorInterval);
}, []);

useEffect(() => {
inputRef.current?.focus();
}, []);

const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
setText(e.target.value);
}, []);

const handleKeyDown = useCallback(
(e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter") {
console.log("Command entered:", text);
setText("");
}
},
[text]
);

return (
<div
className="bg-black font-mono text-lg h-64 overflow-auto mt-4"
onClick={() => inputRef.current?.focus()}
>
<div className="flex">
<span className="mr-2">$</span>
<div className="relative flex-grow">
{text}
<span
className={`absolute inset-y-0 ${
cursorVisible ? "opacity-100" : "opacity-0"
} transition-opacity duration-100`}
style={{ left: `${text.length * 0.61}em` }}
>
</span>
</div>
</div>
<input
ref={inputRef}
type="text"
value={text}
onChange={handleChange}
onKeyDown={handleKeyDown}
className="opacity-0 absolute"
autoFocus
/>
</div>
);
}
134 changes: 3 additions & 131 deletions agent-frontend/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,135 +1,7 @@
'use client';
"use client";

import {
ConnectWallet,
Wallet,
WalletDropdown,
WalletDropdownLink,
WalletDropdownDisconnect,
} from '@coinbase/onchainkit/wallet';
import {
Address,
Avatar,
Name,
Identity,
EthBalance,
} from '@coinbase/onchainkit/identity';
import ArrowSvg from './svg/ArrowSvg';
import ImageSvg from './svg/Image';
import OnchainkitSvg from './svg/OnchainKit';

const components = [
{
name: 'Transaction',
url: 'https://onchainkit.xyz/transaction/transaction',
},
{ name: 'Swap', url: 'https://onchainkit.xyz/swap/swap' },
{ name: 'Checkout', url: 'https://onchainkit.xyz/checkout/checkout' },
{ name: 'Wallet', url: 'https://onchainkit.xyz/wallet/wallet' },
{ name: 'Identity', url: 'https://onchainkit.xyz/identity/identity' },
];

const templates = [
{ name: 'NFT', url: 'https://github.com/coinbase/onchain-app-template' },
{ name: 'Commerce', url: 'https://github.com/coinbase/onchain-commerce-template'},
{ name: 'Fund', url: 'https://github.com/fakepixels/fund-component' },
];
import AgentComponent from "./components/AgentComponent";

export default function App() {
return (
<div className="flex flex-col min-h-screen font-sans dark:bg-background dark:text-white bg-white text-black">
<header className="pt-4 pr-4">
<div className="flex justify-end">
<div className="wallet-container">
<Wallet>
<ConnectWallet>
<Avatar className="h-6 w-6" />
<Name />
</ConnectWallet>
<WalletDropdown>
<Identity className="px-4 pt-3 pb-2" hasCopyAddressOnClick>
<Avatar />
<Name />
<Address />
<EthBalance />
</Identity>
<WalletDropdownLink
icon="wallet"
href="https://keys.coinbase.com"
target="_blank"
rel="noopener noreferrer"
>
Wallet
</WalletDropdownLink>
<WalletDropdownDisconnect />
</WalletDropdown>
</Wallet>
</div>
</div>
</header>

<main className="flex-grow flex items-center justify-center">
<div className="max-w-4xl w-full p-4">
<div className="w-1/3 mx-auto mb-6">
<ImageSvg />
</div>
<div className="flex justify-center mb-6">
<a target="_blank" rel="_template" href="https://onchainkit.xyz">
<OnchainkitSvg className="dark:text-white text-black" />
</a>
</div>
<p className="text-center mb-6">
Get started by editing
<code className="p-1 ml-1 rounded dark:bg-gray-800 bg-gray-200">app/page.tsx</code>.
</p>
<div className="flex flex-col items-center">
<div className="max-w-2xl w-full">
<div className="flex flex-col md:flex-row justify-between mt-4">
<div className="md:w-1/2 mb-4 md:mb-0 flex flex-col items-center">
<p className="font-semibold mb-2 text-center">
Explore components
</p>
<ul className="list-disc pl-5 space-y-2 inline-block text-left">
{components.map((component, index) => (
<li key={index}>
<a
href={component.url}
className="hover:underline inline-flex items-center dark:text-white text-black"
target="_blank"
rel="noopener noreferrer"
>
{component.name}
<ArrowSvg />
</a>
</li>
))}
</ul>
</div>
<div className="md:w-1/2 flex flex-col items-center">
<p className="font-semibold mb-2 text-center">
Explore templates
</p>
<ul className="list-disc pl-5 space-y-2 inline-block text-left">
{templates.map((template, index) => (
<li key={index}>
<a
href={template.url}
className="hover:underline inline-flex items-center dark:text-white text-black"
target="_blank"
rel="noopener noreferrer"
>
{template.name}
<ArrowSvg/>
</a>
</li>
))}
</ul>
</div>
</div>
</div>
</div>
</div>
</main>
</div>
);
return <AgentComponent />;
}
Binary file added agent-frontend/bun.lockb
Binary file not shown.

0 comments on commit 10883de

Please sign in to comment.