feat: added producthunt and peerlist embeds

This commit is contained in:
zaidmukaddam 2024-08-20 21:43:14 +05:30
parent 5d7a20be99
commit d20357fa6a
5 changed files with 160 additions and 106 deletions

View File

@ -386,7 +386,7 @@ export default function Home() {
const MapEmbed = memo(({ location, zoom = 15 }: { location: string, zoom?: number }) => { const MapEmbed = memo(({ location, zoom = 15 }: { location: string, zoom?: number }) => {
const apiKey = process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY; const apiKey = process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY;
const mapUrl = `https://www.google.com/maps/embed/v1/place?key=${apiKey}&q=${encodeURIComponent(location)}&zoom=${zoom}`; const mapUrl = `https://www.google.com/maps/embed/v1/place?key=${apiKey}&q=${encodeURIComponent(location)}&zoom=${zoom}`;
return ( return (
<div className="aspect-video w-full"> <div className="aspect-video w-full">
<iframe <iframe
@ -402,13 +402,13 @@ export default function Home() {
</div> </div>
); );
}); });
MapEmbed.displayName = 'MapEmbed'; MapEmbed.displayName = 'MapEmbed';
const FindPlaceResult = memo(({ result }: { result: any }) => { const FindPlaceResult = memo(({ result }: { result: any }) => {
const place = result.candidates[0]; const place = result.candidates[0];
const location = `${place.geometry.location.lat},${place.geometry.location.lng}`; const location = `${place.geometry.location.lat},${place.geometry.location.lng}`;
return ( return (
<Card className="w-full my-4 overflow-hidden shadow-none"> <Card className="w-full my-4 overflow-hidden shadow-none">
<CardHeader> <CardHeader>
@ -438,13 +438,13 @@ export default function Home() {
</Card> </Card>
); );
}); });
FindPlaceResult.displayName = 'FindPlaceResult'; FindPlaceResult.displayName = 'FindPlaceResult';
const TextSearchResult = memo(({ result }: { result: any }) => { const TextSearchResult = memo(({ result }: { result: any }) => {
const centerLocation = result.results[0]?.geometry?.location; const centerLocation = result.results[0]?.geometry?.location;
const mapLocation = centerLocation ? `${centerLocation.lat},${centerLocation.lng}` : ''; const mapLocation = centerLocation ? `${centerLocation.lat},${centerLocation.lng}` : '';
return ( return (
<Card className="w-full my-4 overflow-hidden shadow-none"> <Card className="w-full my-4 overflow-hidden shadow-none">
<CardHeader> <CardHeader>
@ -484,9 +484,9 @@ export default function Home() {
</Card> </Card>
); );
}); });
TextSearchResult.displayName = 'TextSearchResult'; TextSearchResult.displayName = 'TextSearchResult';
const renderToolInvocation = (toolInvocation: ToolInvocation, index: number) => { const renderToolInvocation = (toolInvocation: ToolInvocation, index: number) => {
const args = JSON.parse(JSON.stringify(toolInvocation.args)); const args = JSON.parse(JSON.stringify(toolInvocation.args));
@ -560,7 +560,7 @@ export default function Home() {
</Card> </Card>
); );
} }
if (toolInvocation.toolName === 'find_place') { if (toolInvocation.toolName === 'find_place') {
if (!result) { if (!result) {
return ( return (
@ -588,10 +588,10 @@ export default function Home() {
</div> </div>
); );
} }
return <FindPlaceResult result={result} />; return <FindPlaceResult result={result} />;
} }
if (toolInvocation.toolName === 'text_search') { if (toolInvocation.toolName === 'text_search') {
if (!result) { if (!result) {
return ( return (
@ -619,7 +619,7 @@ export default function Home() {
</div> </div>
); );
} }
return <TextSearchResult result={result} />; return <TextSearchResult result={result} />;
} }
@ -738,39 +738,39 @@ export default function Home() {
</div> </div>
</TabsContent> </TabsContent>
{result?.images && result.images.length > 0 && ( {result?.images && result.images.length > 0 && (
<TabsContent value="images" className="p-0 m-0 bg-white"> <TabsContent value="images" className="p-0 m-0 bg-white">
<div className="space-y-4 p-4"> <div className="space-y-4 p-4">
{result.images.map((img: { format: 'png' | 'jpeg' | 'svg', data: string }, imgIndex: number) => ( {result.images.map((img: { format: 'png' | 'jpeg' | 'svg', data: string }, imgIndex: number) => (
<div key={imgIndex} className="space-y-2"> <div key={imgIndex} className="space-y-2">
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
<h4 className="text-sm font-medium">Image {imgIndex + 1}</h4> <h4 className="text-sm font-medium">Image {imgIndex + 1}</h4>
<Button <Button
variant="ghost" variant="ghost"
size="sm" size="sm"
className="p-0 h-8 w-8" className="p-0 h-8 w-8"
onClick={() => { onClick={() => {
const link = document.createElement('a'); const link = document.createElement('a');
link.href = `data:image/${img.format === 'svg' ? 'svg+xml' : img.format};base64,${img.data}`; link.href = `data:image/${img.format === 'svg' ? 'svg+xml' : img.format};base64,${img.data}`;
link.download = `generated-image-${imgIndex + 1}.${img.format}`; link.download = `generated-image-${imgIndex + 1}.${img.format}`;
link.click(); link.click();
}} }}
> >
<Download className="h-4 w-4" /> <Download className="h-4 w-4" />
</Button> </Button>
</div>
<div className="relative w-full" style={{ aspectRatio: '16/9' }}>
<Image
src={`data:image/${img.format === 'svg' ? 'svg+xml' : img.format};base64,${img.data}`}
alt={`Generated image ${imgIndex + 1}`}
layout="fill"
objectFit="contain"
/>
</div>
</div> </div>
<div className="relative w-full" style={{ aspectRatio: '16/9' }}> ))}
<Image </div>
src={`data:image/${img.format === 'svg' ? 'svg+xml' : img.format};base64,${img.data}`} </TabsContent>
alt={`Generated image ${imgIndex + 1}`} )}
layout="fill"
objectFit="contain"
/>
</div>
</div>
))}
</div>
</TabsContent>
)}
</Tabs> </Tabs>
</div> </div>
); );
@ -1131,16 +1131,34 @@ export default function Home() {
<Navbar /> <Navbar />
<div className={`w-full max-w-[90%] sm:max-w-2xl space-y-6 p-1 ${hasSubmitted ? 'mt-16 sm:mt-20' : 'mt-[26vh] sm:mt-[30vh]'}`}> <div className={`w-full max-w-[90%] sm:max-w-2xl space-y-6 p-1 ${hasSubmitted ? 'mt-16 sm:mt-20' : 'mt-[26vh] sm:mt-[30vh]'}`}>
{!hasSubmitted && {!hasSubmitted && (
<div <div className="text-center">
className="text-center"
>
<h1 className="text-4xl sm:text-6xl mb-1 text-primary font-serif">MiniPerplx</h1> <h1 className="text-4xl sm:text-6xl mb-1 text-primary font-serif">MiniPerplx</h1>
<h2 className='text-xl sm:text-2xl font-serif text-balance text-center mb-6'> <h2 className='text-xl sm:text-2xl font-serif text-balance text-center mb-6'>
In search for minimalism and simplicity In search for minimalism and simplicity
</h2> </h2>
<div className="flex justify-center items-center space-x-4 mb-6">
<Link href="https://www.producthunt.com/posts/miniperplx?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-miniperplx" target="_blank" rel="noopener noreferrer" passHref>
<Image
src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=481378&theme=light"
alt="MiniPerplx - A minimalistic AI-powered search engine. | Product Hunt"
width={250}
height={54}
className="h-auto w-auto"
/>
</Link>
<Link href="https://peerlist.io/zaidmukaddam/project/miniperplx" target="_blank" rel="noopener noreferrer" passHref>
<Image
src="/Launch_SVG_Light.svg"
alt="Peerlist"
width={32}
height={32}
className="h-auto w-auto"
/>
</Link>
</div>
</div> </div>
} )}
<AnimatePresence> <AnimatePresence>
{!hasSubmitted && ( {!hasSubmitted && (
<motion.div <motion.div

View File

@ -1,12 +1,23 @@
/** @type {import('next').NextConfig} */ /** @type {import('next').NextConfig} */
const nextConfig = { const nextConfig = {
images: { images: {
remotePatterns: [{ dangerouslyAllowSVG: true,
protocol: 'https', remotePatterns: [
hostname: 'www.google.com', {
port: '', protocol: 'https',
pathname: '/s2/favicons', hostname: 'www.google.com',
}] port: '',
pathname: '/s2/favicons',
},
// https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=481378&theme=light
{
protocol: 'https',
hostname: 'api.producthunt.com',
port: '',
pathname: '/widgets/embed-image/v1/featured.svg',
},
]
} }
}; };

View File

@ -9,7 +9,6 @@
"lint": "next lint" "lint": "next lint"
}, },
"dependencies": { "dependencies": {
"@ai-sdk/anthropic": "latest",
"@ai-sdk/openai": "^0.0.46", "@ai-sdk/openai": "^0.0.46",
"@e2b/code-interpreter": "^0.0.8", "@e2b/code-interpreter": "^0.0.8",
"@radix-ui/react-accordion": "^1.2.0", "@radix-ui/react-accordion": "^1.2.0",

View File

@ -5,9 +5,6 @@ settings:
excludeLinksFromLockfile: false excludeLinksFromLockfile: false
dependencies: dependencies:
'@ai-sdk/anthropic':
specifier: latest
version: 0.0.43(zod@3.23.8)
'@ai-sdk/openai': '@ai-sdk/openai':
specifier: ^0.0.46 specifier: ^0.0.46
version: 0.0.46(zod@3.23.8) version: 0.0.46(zod@3.23.8)
@ -55,7 +52,7 @@ dependencies:
version: 1.4.0 version: 1.4.0
ai: ai:
specifier: latest specifier: latest
version: 3.3.11(react@18.3.1)(svelte@4.2.18)(vue@3.4.35)(zod@3.23.8) version: 3.3.12(react@18.3.1)(svelte@4.2.18)(vue@3.4.35)(zod@3.23.8)
class-variance-authority: class-variance-authority:
specifier: ^0.7.0 specifier: ^0.7.0
version: 0.7.0 version: 0.7.0
@ -139,17 +136,6 @@ devDependencies:
packages: packages:
/@ai-sdk/anthropic@0.0.43(zod@3.23.8):
resolution: {integrity: sha512-X9GBINVeaynnpSETD6rBgkmkR5VynceyVe8FxDCM+5jQF9fNNPCpaXX/syE9G8HH941HGFdEcFuGuLR6MvNdeA==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.0.0
dependencies:
'@ai-sdk/provider': 0.0.20
'@ai-sdk/provider-utils': 1.0.13(zod@3.23.8)
zod: 3.23.8
dev: false
/@ai-sdk/openai@0.0.46(zod@3.23.8): /@ai-sdk/openai@0.0.46(zod@3.23.8):
resolution: {integrity: sha512-RdE/PjdGt5zeE19qd/aqEr6fz6lhA5IWzBMy+N3dkdGtijT+eJP7Kxta6lYwnevkr1yqRPXYkYX/6v/s6xVY4A==} resolution: {integrity: sha512-RdE/PjdGt5zeE19qd/aqEr6fz6lhA5IWzBMy+N3dkdGtijT+eJP7Kxta6lYwnevkr1yqRPXYkYX/6v/s6xVY4A==}
engines: {node: '>=18'} engines: {node: '>=18'}
@ -177,8 +163,8 @@ packages:
zod: 3.23.8 zod: 3.23.8
dev: false dev: false
/@ai-sdk/provider-utils@1.0.13(zod@3.23.8): /@ai-sdk/provider-utils@1.0.14(zod@3.23.8):
resolution: {integrity: sha512-NDQUUBDQoWk9aGn2pOA5wiM5CdO57KeYTEph7PpKGEU8IyqI0d+CiYKISOia6Omy17d+Dw/ZM6KP98F89BGJ5A==} resolution: {integrity: sha512-6jKYgg/iitJiz9ivlTx1CDrQBx1BeSd0IlRJ/Fl5LcdGAc3gnsMVR+R1w1jxzyhjVyh6g+NqlOZenW0tctNZnA==}
engines: {node: '>=18'} engines: {node: '>=18'}
peerDependencies: peerDependencies:
zod: ^3.0.0 zod: ^3.0.0
@ -186,7 +172,7 @@ packages:
zod: zod:
optional: true optional: true
dependencies: dependencies:
'@ai-sdk/provider': 0.0.20 '@ai-sdk/provider': 0.0.21
eventsource-parser: 1.1.2 eventsource-parser: 1.1.2
nanoid: 3.3.6 nanoid: 3.3.6
secure-json-parse: 2.7.0 secure-json-parse: 2.7.0
@ -200,15 +186,15 @@ packages:
json-schema: 0.4.0 json-schema: 0.4.0
dev: false dev: false
/@ai-sdk/provider@0.0.20: /@ai-sdk/provider@0.0.21:
resolution: {integrity: sha512-nCQZRUTi/+y+kf1ep9rujpbQEtsIwySzlQAudiFeVhzzDi9rYvWp5tOSVu8/ArT+i1xSc2tw40akxb1TX73ofQ==} resolution: {integrity: sha512-9j95uaPRxwYkzQdkl4XO/MmWWW5c5vcVSXtqvALpD9SMB9fzH46dO3UN4VbOJR2J3Z84CZAqgZu5tNlkptT9qQ==}
engines: {node: '>=18'} engines: {node: '>=18'}
dependencies: dependencies:
json-schema: 0.4.0 json-schema: 0.4.0
dev: false dev: false
/@ai-sdk/react@0.0.46(react@18.3.1)(zod@3.23.8): /@ai-sdk/react@0.0.47(react@18.3.1)(zod@3.23.8):
resolution: {integrity: sha512-MQHC6AxEUi10++8LsnR3NUK+VkxB2sEWVlFxYrj6PKF5krxWnLgoCmpvbAdM6oc5b9byySBJaj6ZxRxutRpWtQ==} resolution: {integrity: sha512-lvH5vscfiH93zbJCTR4GZoX0FuiCShEKZVYVQSaE8wod+lc/LaFp9u7sXgHeZOCQj06C68WB88h90V2l9P6cig==}
engines: {node: '>=18'} engines: {node: '>=18'}
peerDependencies: peerDependencies:
react: ^18 || ^19 react: ^18 || ^19
@ -219,15 +205,15 @@ packages:
zod: zod:
optional: true optional: true
dependencies: dependencies:
'@ai-sdk/provider-utils': 1.0.13(zod@3.23.8) '@ai-sdk/provider-utils': 1.0.14(zod@3.23.8)
'@ai-sdk/ui-utils': 0.0.33(zod@3.23.8) '@ai-sdk/ui-utils': 0.0.34(zod@3.23.8)
react: 18.3.1 react: 18.3.1
swr: 2.2.5(react@18.3.1) swr: 2.2.5(react@18.3.1)
zod: 3.23.8 zod: 3.23.8
dev: false dev: false
/@ai-sdk/solid@0.0.36(zod@3.23.8): /@ai-sdk/solid@0.0.37(zod@3.23.8):
resolution: {integrity: sha512-pm57goMQczSpPTNrUrwbab5BybZYofBRZ10UkTi2KgJP5i+S/sGHSh/xtgZz+xNpUt42pk8aYvOiNDN1ppjkDA==} resolution: {integrity: sha512-KfRHEjBNmtm78Ch1MmTjuvUb7EHOCWiZrypqZt+R0EZJ6X9SIy4fwlXpY/Mn5WuOweAK7vL+woIAlk0SIRSF0w==}
engines: {node: '>=18'} engines: {node: '>=18'}
peerDependencies: peerDependencies:
solid-js: ^1.7.7 solid-js: ^1.7.7
@ -235,14 +221,14 @@ packages:
solid-js: solid-js:
optional: true optional: true
dependencies: dependencies:
'@ai-sdk/provider-utils': 1.0.13(zod@3.23.8) '@ai-sdk/provider-utils': 1.0.14(zod@3.23.8)
'@ai-sdk/ui-utils': 0.0.33(zod@3.23.8) '@ai-sdk/ui-utils': 0.0.34(zod@3.23.8)
transitivePeerDependencies: transitivePeerDependencies:
- zod - zod
dev: false dev: false
/@ai-sdk/svelte@0.0.38(svelte@4.2.18)(zod@3.23.8): /@ai-sdk/svelte@0.0.39(svelte@4.2.18)(zod@3.23.8):
resolution: {integrity: sha512-sTNkxzhS1B0TDdVWZR6yXG+3qQGYAxMwcEKzMVjm1VdpGlZits1PxF39aVvPldaWM8QB4MrVE+H5b5dTA43D0Q==} resolution: {integrity: sha512-7t/DfxlsWqA+3gDNEUX9ONgdzCpPfPKzkxsE3UGtHvrRLknbLa692yNGJNi0OVB5V++vhtpPw4LViPEmj6DV2w==}
engines: {node: '>=18'} engines: {node: '>=18'}
peerDependencies: peerDependencies:
svelte: ^3.0.0 || ^4.0.0 svelte: ^3.0.0 || ^4.0.0
@ -250,16 +236,16 @@ packages:
svelte: svelte:
optional: true optional: true
dependencies: dependencies:
'@ai-sdk/provider-utils': 1.0.13(zod@3.23.8) '@ai-sdk/provider-utils': 1.0.14(zod@3.23.8)
'@ai-sdk/ui-utils': 0.0.33(zod@3.23.8) '@ai-sdk/ui-utils': 0.0.34(zod@3.23.8)
sswr: 2.1.0(svelte@4.2.18) sswr: 2.1.0(svelte@4.2.18)
svelte: 4.2.18 svelte: 4.2.18
transitivePeerDependencies: transitivePeerDependencies:
- zod - zod
dev: false dev: false
/@ai-sdk/ui-utils@0.0.33(zod@3.23.8): /@ai-sdk/ui-utils@0.0.34(zod@3.23.8):
resolution: {integrity: sha512-2oZjZzZG3AGQihO1d3mWqgFuywTtjBtkUEeE7d8nicw3QQv9m1MwrbQqRhhKbbBetBke6V9o5FQ5wngmb/+3iw==} resolution: {integrity: sha512-8nTBsQklLrp6r/AJyeWxD8D4pvhQhlGfrDaBAfo7OEdBLwF6bNdnh6CJFSwUMiXUtUPOBqPV5tFgY2pJmGQikg==}
engines: {node: '>=18'} engines: {node: '>=18'}
peerDependencies: peerDependencies:
zod: ^3.0.0 zod: ^3.0.0
@ -267,16 +253,16 @@ packages:
zod: zod:
optional: true optional: true
dependencies: dependencies:
'@ai-sdk/provider': 0.0.20 '@ai-sdk/provider': 0.0.21
'@ai-sdk/provider-utils': 1.0.13(zod@3.23.8) '@ai-sdk/provider-utils': 1.0.14(zod@3.23.8)
json-schema: 0.4.0 json-schema: 0.4.0
secure-json-parse: 2.7.0 secure-json-parse: 2.7.0
zod: 3.23.8 zod: 3.23.8
zod-to-json-schema: 3.22.5(zod@3.23.8) zod-to-json-schema: 3.22.5(zod@3.23.8)
dev: false dev: false
/@ai-sdk/vue@0.0.38(vue@3.4.35)(zod@3.23.8): /@ai-sdk/vue@0.0.39(vue@3.4.35)(zod@3.23.8):
resolution: {integrity: sha512-iqVPsRDXkrfIFzwrWoUKqBzMqSHxJQoompdj0LCC9v3s9c4ndn9Vx67nB5g2ee3fO3bY/O9vLebDwYyVKM4glg==} resolution: {integrity: sha512-lIcDV1PieneN6yxb7LDdYmXEOearRxdvAgVvMdtAS+Fc4s8basaidh6AA6OFOPAVc16PchpfZCVREfve0r6pUw==}
engines: {node: '>=18'} engines: {node: '>=18'}
peerDependencies: peerDependencies:
vue: ^3.3.4 vue: ^3.3.4
@ -284,8 +270,8 @@ packages:
vue: vue:
optional: true optional: true
dependencies: dependencies:
'@ai-sdk/provider-utils': 1.0.13(zod@3.23.8) '@ai-sdk/provider-utils': 1.0.14(zod@3.23.8)
'@ai-sdk/ui-utils': 0.0.33(zod@3.23.8) '@ai-sdk/ui-utils': 0.0.34(zod@3.23.8)
swrv: 1.0.4(vue@3.4.35) swrv: 1.0.4(vue@3.4.35)
vue: 3.4.35(typescript@5.5.4) vue: 3.4.35(typescript@5.5.4)
transitivePeerDependencies: transitivePeerDependencies:
@ -1606,8 +1592,8 @@ packages:
engines: {node: '>=0.4.0'} engines: {node: '>=0.4.0'}
hasBin: true hasBin: true
/ai@3.3.11(react@18.3.1)(svelte@4.2.18)(vue@3.4.35)(zod@3.23.8): /ai@3.3.12(react@18.3.1)(svelte@4.2.18)(vue@3.4.35)(zod@3.23.8):
resolution: {integrity: sha512-KPfikv3KDqFbZXRoB3jaM6bRJGU2ivmk12ahmo8LynEq1zqJJPc/2JEqlXNG9MoAQo3tQuqYiQfDzPIEO4aObA==} resolution: {integrity: sha512-vmum83qRAWNPWFiaxhQNMAksZhjcSFdGlUdQubifCvYS1wptDFxz9WyLgh4hwUPiJNX5sNuUqra6U1VndLZ6Aw==}
engines: {node: '>=18'} engines: {node: '>=18'}
peerDependencies: peerDependencies:
openai: ^4.42.0 openai: ^4.42.0
@ -1627,13 +1613,13 @@ packages:
zod: zod:
optional: true optional: true
dependencies: dependencies:
'@ai-sdk/provider': 0.0.20 '@ai-sdk/provider': 0.0.21
'@ai-sdk/provider-utils': 1.0.13(zod@3.23.8) '@ai-sdk/provider-utils': 1.0.14(zod@3.23.8)
'@ai-sdk/react': 0.0.46(react@18.3.1)(zod@3.23.8) '@ai-sdk/react': 0.0.47(react@18.3.1)(zod@3.23.8)
'@ai-sdk/solid': 0.0.36(zod@3.23.8) '@ai-sdk/solid': 0.0.37(zod@3.23.8)
'@ai-sdk/svelte': 0.0.38(svelte@4.2.18)(zod@3.23.8) '@ai-sdk/svelte': 0.0.39(svelte@4.2.18)(zod@3.23.8)
'@ai-sdk/ui-utils': 0.0.33(zod@3.23.8) '@ai-sdk/ui-utils': 0.0.34(zod@3.23.8)
'@ai-sdk/vue': 0.0.38(vue@3.4.35)(zod@3.23.8) '@ai-sdk/vue': 0.0.39(vue@3.4.35)(zod@3.23.8)
'@opentelemetry/api': 1.9.0 '@opentelemetry/api': 1.9.0
eventsource-parser: 1.1.2 eventsource-parser: 1.1.2
json-schema: 0.4.0 json-schema: 0.4.0

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 39 KiB