Skip to content

Commit

Permalink
Merge branch 'main' of github.com:GuiBibeau/akeru-search
Browse files Browse the repository at this point in the history
  • Loading branch information
TommieNg committed Sep 18, 2024
2 parents 5a32fe9 + 32227a6 commit bec71f9
Show file tree
Hide file tree
Showing 12 changed files with 673 additions and 74 deletions.
2 changes: 1 addition & 1 deletion app/api/search/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export async function GET(request: NextRequest) {

const encodedQuery = encodeURIComponent(query);
const response = await fetch(
`https://agents-api-312977769596.northamerica-northeast2.run.app/search?q=${encodedQuery}&stream=true`,
`http://localhost:8080/search?q=${encodedQuery}&stream=true`,
{
headers: {
Authorization: `Bearer ${process.env.AGENTS_API_KEY}`,
Expand Down
5 changes: 0 additions & 5 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
import { NavBar } from "@/components/NavBar";
import { SearchBar } from "./SearchBar";

export interface SearchResult {
url: string;
title: string;
}

export default async function Home() {
return (
<div className="min-h-screen bg-black text-white flex flex-col">
Expand Down
79 changes: 64 additions & 15 deletions app/search/SearchResults.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
import { useContext } from "react";
import { Card, CardContent, CardTitle } from "@/components/ui/card";
import { SearchContext } from "./searchProvider";
import { FileIcon } from "lucide-react"; // Import icons
import { LinkPreview } from "@/components/ui/link-preview";
import Image from "next/image";

export const experimental_ppr = true;

Expand All @@ -11,35 +14,81 @@ export function SearchResults() {
if (!searchContext)
throw new Error("SearchResults must be used within a SearchProvider");

const { searchResults } = searchContext;
const { searchResults, isSearching } = searchContext;

// Add this condition to hide results when empty
if (searchResults.length === 0) {
const renderSkeletons = () => (
<div className="w-full">
<h2 className="text-xl font-bold mb-2 text-gray-300">Sources</h2>
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
{[...Array(3)].map((_, index) => (
<Card key={index} className="h-24 bg-black-800 border-gray-700">
{" "}
<CardContent className="p-3 flex flex-col">
<div className="w-1/2 h-3 bg-gray-700 rounded mb-1 animate-pulse"></div>
<div className="w-3/4 h-2 bg-gray-700 rounded mb-2 animate-pulse"></div>
<div className="flex-grow space-y-1">
<div className="w-full h-2 bg-gray-700 rounded animate-pulse"></div>
<div className="w-full h-2 bg-gray-700 rounded animate-pulse"></div>
</div>
<div className="flex justify-end mt-2">
<div className="w-1/4 h-2 bg-gray-700 rounded animate-pulse"></div>
</div>
</CardContent>
</Card>
))}
</div>
</div>
);

if (isSearching && searchResults.length === 0) {
return renderSkeletons();
}

if (!isSearching && searchResults.length === 0) {
return null;
}

return (
<div className="w-full">
<h2 className="text-xl font-bold mb-2 text-gray-300">Sources</h2>
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
{searchResults.map((result, index) => (
<a
key={index}
href={result.url}
target="_blank"
rel="noopener noreferrer"
className="block h-full"
className="no-underline"
>
<Card className="bg-black-800 border-gray-700 hover:bg-black-700 transition-colors duration-200 h-full flex flex-col">
<CardContent className="p-4 flex flex-col flex-grow">
<CardTitle className="text-sm text-gray-200 mb-2 line-clamp-2">
{result.title}
</CardTitle>
<span className="text-blue-400 hover:text-blue-300 text-xs inline-block mt-auto">
Read more
</span>
</CardContent>
</Card>
<LinkPreview
url={result.url}
className="font-bold bg-clip-text text-transparent bg-gradient-to-br from-purple-500 to-pink-500"
>
<Card className="bg-black-800 border-gray-700 hover:bg-black-700 transition-colors duration-200 h-24 flex flex-col relative">
<CardContent className="p-3 flex flex-col flex-grow">
{result.icon ? (
<Image
src={result.icon}
alt="Icon"
width={16}
height={16}
className="absolute top-2 right-2 w-4 h-4"
/>
) : (
<FileIcon className="absolute top-2 right-2 w-4 h-4 text-gray-400" />
)}
<CardTitle className="text-xs text-gray-200 mb-1 line-clamp-1">
{result.title}
</CardTitle>
<div className="text-xs text-gray-400 mb-1 truncate">
<span>{result.source}</span><span>{result.date}</span>
</div>
<p className="text-xs text-gray-300 line-clamp-2">
{result.summary.replace(/<[^>]*>/g, "")}
</p>
</CardContent>
</Card>
</LinkPreview>
</a>
))}
</div>
Expand Down
54 changes: 22 additions & 32 deletions app/search/Summary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import { useContext } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
import { SearchContext } from "./searchProvider";

export function Summary() {
Expand All @@ -19,37 +18,28 @@ export function Summary() {

return (
<div className="w-full mb-4 mt-4">
<Card className="bg-black border-gray-700 relative z-10">
<CardHeader>
<CardTitle className="text-xl font-bold text-gray-300">
Summary
</CardTitle>
</CardHeader>
<CardContent>
{isLoadingSummary ? (
<div className="flex justify-center items-center h-16">
<div className="animate-spin rounded-full h-6 w-6 border-t-2 border-b-2 border-gray-300"></div>
</div>
) : (
<p className="text-base text-gray-300">
<AnimatePresence mode="popLayout">
{displayedWords.map((word, index) => (
<motion.span
key={index}
initial={{ opacity: 0, y: 5 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.1 }}
className="inline-block mr-1"
>
{word}
</motion.span>
))}
</AnimatePresence>
</p>
)}
</CardContent>
</Card>
{isLoadingSummary ? (
<div className="flex justify-center items-center h-16">
<div className="animate-spin rounded-full h-6 w-6 border-t-2 border-b-2 border-gray-300"></div>
</div>
) : (
<p className="text-base text-gray-300">
<AnimatePresence mode="popLayout">
{displayedWords.map((word, index) => (
<motion.span
key={index}
initial={{ opacity: 0, y: 5 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.1 }}
className="inline-block mr-1"
>
{word}
</motion.span>
))}
</AnimatePresence>
</p>
)}
</div>
);
}
31 changes: 22 additions & 9 deletions app/search/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,6 @@ import { SearchProvider } from "./searchProvider";
import { SearchResults } from "./SearchResults";
import { Summary } from "./Summary";

export interface SearchResult {
url: string;
title: string;
}

export default async function Home({
searchParams,
}: {
Expand All @@ -19,10 +14,28 @@ export default async function Home({
<SearchProvider initialQuery={initialQuery}>
<div className="min-h-screen bg-black text-white flex flex-col">
<NavBar />
<div className="flex-grow flex flex-col items-center p-4">
<div className="mt-24 w-full max-w-6xl flex flex-col">
<SearchResults />
<Summary />
<div className="flex-grow flex justify-center p-4">
<div className="w-full max-w-3xl flex flex-col items-center">
<div className="w-full flex flex-col items-start">
<p className="text-sm text-gray-400 mb-1">
Powered by{" "}
<a
href="https://www.gaianet.ai/"
target="_blank"
rel="noopener noreferrer"
className="text-blue-400 font-semibold hover:text-blue-300 hover:underline transition-colors duration-200"
>
Gaia Living Systems
</a>
</p>
<h1 className="text-3xl font-bold text-gray-300">
{initialQuery}
</h1>
</div>
<div className="mt-6 w-full flex flex-col">
<SearchResults />
<Summary />
</div>
</div>
</div>
</div>
Expand Down
11 changes: 10 additions & 1 deletion app/search/searchProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"use client";

import { SearchResult } from "@/app/page";
import React, {
createContext,
useState,
Expand All @@ -10,6 +9,16 @@ import React, {
} from "react";
import { useRouter } from "next/navigation";

export interface SearchResult {
url: string;
title: string;
source: string;
date: string;
summary: string;
keyPoints: string[];
icon: string;
thumbnailSrc?: string;
}
interface SearchContextType {
query: string;
setQuery: (query: string) => void;
Expand Down
28 changes: 19 additions & 9 deletions components/NavBar.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,29 @@
import Link from "next/link";
import Image from "next/image";

export function NavBar() {
return (
<nav className="sticky top-0 z-50 bg-black text-white p-4 border-b border-white">
<nav className="sticky top-0 z-50 bg-black text-white p-4 border-b border-white/50">
<div className="max-w-6xl mx-auto flex justify-between items-center">
<Link href="/" className="text-xl font-bold">
Akeru Search
<Link href="/" className="flex items-center space-x-2">
<Image
src="/logo.jpeg"
alt="Akeru Search Logo"
width={40}
height={40}
className="rounded-full"
/>
<span className="text-xl font-bold">Akeru Search</span>
</Link>
<div className="space-x-4">
<Link href="/" className="hover:text-gray-300">
Home
</Link>
<Link href="/about" className="hover:text-gray-300">
About
</Link>
<a
href="https://github.com/GuiBibeau/akeru-search"
target="_blank"
rel="noopener noreferrer"
className="inline-block py-2 px-4 bg-transparent text-white font-semibold rounded-lg border border-white/50 hover:bg-white hover:text-black transition duration-300 ease-in-out"
>
⭐ Star on GitHub
</a>
</div>
</div>
</nav>
Expand Down
Loading

0 comments on commit bec71f9

Please sign in to comment.