diff --git a/app/actions/search.ts b/app/actions/search.ts index fc7d478..9b0a3c7 100644 --- a/app/actions/search.ts +++ b/app/actions/search.ts @@ -5,7 +5,10 @@ import { Search } from "@upstash/search"; import type { PutBlobResult } from "@vercel/blob"; -const upstash = Search.fromEnv(); +const upstash = new Search({ + url: process.env.UPSTASH_SEARCH_REST_URL!, + token: process.env.UPSTASH_SEARCH_REST_TOKEN!, +}); const index = upstash.index("images"); type SearchResponse = diff --git a/app/api/upload/generate-description.ts b/app/api/upload/generate-description.ts index 630346b..1d30e10 100644 --- a/app/api/upload/generate-description.ts +++ b/app/api/upload/generate-description.ts @@ -23,7 +23,17 @@ export const generateDescription = async (blob: PutBlobResult) => { const { text } = await generateText({ model: "xai/grok-2-vision", - system: "Describe the image in detail.", + system: `You are an image description expert. Describe the image in detail for a searchable image database. + +Include the following when relevant: +- People: gender, approximate age, clothing (colors, types), accessories, hair, expressions +- Objects: what they are, colors, sizes, brands if visible +- Setting: indoor/outdoor, location type, time of day +- Actions: what is happening, poses, activities +- Colors: mention prominent colors explicitly +- Text: any visible text or signs + +Be specific and use common search terms. For example, say "man in blue t-shirt" not just "person wearing clothes".`, messages: [ { role: "user", diff --git a/app/api/upload/index-image.ts b/app/api/upload/index-image.ts index 2e18531..6357e00 100644 --- a/app/api/upload/index-image.ts +++ b/app/api/upload/index-image.ts @@ -4,7 +4,10 @@ import { Search } from "@upstash/search"; import type { PutBlobResult } from "@vercel/blob"; import { FatalError, getStepMetadata, RetryableError } from "workflow"; -const upstash = Search.fromEnv(); +const upstash = new Search({ + url: process.env.UPSTASH_SEARCH_REST_URL!, + token: process.env.UPSTASH_SEARCH_REST_TOKEN!, +}); export const indexImage = async (blob: PutBlobResult, text: string) => { "use step"; diff --git a/app/layout.tsx b/app/layout.tsx index 6f05284..351f5fd 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -21,8 +21,8 @@ const mono = Geist_Mono({ }); export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", + title: "Picsearch", + description: "Search your photos using natural language", }; type RootLayoutProps = { diff --git a/app/page.tsx b/app/page.tsx index ba80e4b..d308be9 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -5,8 +5,8 @@ import { Results } from "@/components/results"; import { UploadedImagesProvider } from "@/components/uploaded-images-provider"; export const metadata: Metadata = { - title: "vectr", - description: "vectr", + title: "Picsearch", + description: "Search your photos using natural language", }; const ImagesSkeleton = () => ( @@ -29,14 +29,14 @@ const ImagesSkeleton = () => ( const Home = () => ( -
+
}> -
+
); diff --git a/components/deploy.tsx b/components/deploy.tsx deleted file mode 100644 index 5e946c7..0000000 --- a/components/deploy.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import Image from "next/image"; - -export const DeployButton = () => { - const url = new URL("https://vercel.com/new/clone"); - - // Demo - url.searchParams.set( - "demo-description", - "A free, open-source template for building natural language image search on the AI Cloud." - ); - url.searchParams.set("demo-image", "https://vectr.store/opengraph-image.png"); - url.searchParams.set("demo-title", "vectr.store"); - url.searchParams.set("demo-url", "https://vectr.store/"); - - // Marketplace - url.searchParams.set("from", "templates"); - url.searchParams.set("project-name", "Vectr"); - - // Repository - url.searchParams.set("repository-name", "vectr"); - url.searchParams.set("repository-url", "https://github.com/vercel/vectr"); - - // Integrations - url.searchParams.set( - "products", - JSON.stringify([ - { - type: "integration", - protocol: "storage", - productSlug: "upstash-search", - integrationSlug: "upstash", - }, - { type: "blob" }, - ]) - ); - url.searchParams.set("skippable-integrations", "0"); - - return ( - - Deploy with Vercel - - ); -}; diff --git a/components/header.tsx b/components/header.tsx index d13256e..0566a2c 100644 --- a/components/header.tsx +++ b/components/header.tsx @@ -1,20 +1,17 @@ import { CheckCircle2Icon, ImageUpIcon } from "lucide-react"; -import { DeployButton } from "./deploy"; -import { Button } from "./ui/button"; export const Header = () => (
-

vectr.store

+

Picsearch

- A free, open-source template for building natural language image search - on the AI Cloud. + Search your photos using natural language. Just describe what you're looking for.

- Try searching for "water" or "desert". + Try searching for "dog" or "blue shirt".

-
- - -
); diff --git a/components/preview.tsx b/components/preview.tsx index 09ea3c0..d3b36d1 100644 --- a/components/preview.tsx +++ b/components/preview.tsx @@ -9,10 +9,10 @@ export const Preview = ({ url, priority }: PreviewProps) => (
{url} diff --git a/components/results.client.tsx b/components/results.client.tsx index ea1c4ec..169499d 100644 --- a/components/results.client.tsx +++ b/components/results.client.tsx @@ -109,6 +109,7 @@ export const ResultsClient = ({ defaultData }: ResultsClientProps) => { variant="ghost" > + Clear search results )} { {isPending ? ( ) : ( diff --git a/components/ui/sonner.tsx b/components/ui/sonner.tsx index 957524e..93665cf 100644 --- a/components/ui/sonner.tsx +++ b/components/ui/sonner.tsx @@ -1,14 +1,11 @@ -"use client" +"use client"; -import { useTheme } from "next-themes" -import { Toaster as Sonner, ToasterProps } from "sonner" - -const Toaster = ({ ...props }: ToasterProps) => { - const { theme = "system" } = useTheme() +import { Toaster as Sonner, type ToasterProps } from "sonner"; +function Toaster(props: ToasterProps) { return ( { } {...props} /> - ) + ); } -export { Toaster } +export { Toaster }; diff --git a/components/upload-button.tsx b/components/upload-button.tsx index b5c7c19..7ab008e 100644 --- a/components/upload-button.tsx +++ b/components/upload-button.tsx @@ -12,9 +12,6 @@ export const UploadButton = () => { const inputRef = useRef(null); const abortControllerRef = useRef(null); const [isUploading, setIsUploading] = useState(false); - const isDemo = - typeof window !== "undefined" && - window.location.hostname.includes("vectr.store"); const cancelUpload = () => { if (abortControllerRef.current) { @@ -31,11 +28,6 @@ export const UploadButton = () => { return; } - if (isDemo) { - toast.error("Uploads are disabled in demo mode"); - return; - } - // Check file sizes const maxSize = 4.5 * 1024 * 1024; // 4.5MB const oversizedFiles = files.filter((file) => file.size > maxSize); @@ -239,13 +231,14 @@ export const UploadButton = () => { /> ); diff --git a/package.json b/package.json index 36ef0b1..f2b90c8 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "dependencies": { "@hookform/resolvers": "^5.2.2", "@upstash/search": "^0.1.5", + "@upstash/vector": "^1.2.2", "@vercel/analytics": "^1.5.0", "@vercel/blob": "^2.0.0", "ai": "^5.0.77", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9478fb0..ea89f40 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,9 @@ importers: '@upstash/search': specifier: ^0.1.5 version: 0.1.5 + '@upstash/vector': + specifier: ^1.2.2 + version: 1.2.2 '@vercel/analytics': specifier: ^1.5.0 version: 1.5.0(next@16.0.10(@opentelemetry/api@1.9.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0)