Compare commits

..

No commits in common. "d468ecca7485fbab512266910c14cca5daf45c2d" and "186bd3c2ccfc916ef63041ab323ac795caa3ebc7" have entirely different histories.

13 changed files with 255 additions and 386 deletions

View File

@ -1,28 +1,5 @@
# Strictly Server side Env variables ANTHROPIC_API_KEY=sk-ant-api****
XAI_API_KEY=
UPSTASH_REDIS_REST_URL=
UPSTASH_REDIS_REST_TOKEN=
AVIATION_STACK_API_KEY=
SANDBOX_TEMPLATE_ID=
TMDB_API_KEY=
YT_ENDPOINT=
EXA_API_KEY=
TRIPADVISOR_API_KEY=
BLOB_READ_WRITE_TOKEN=
ELEVENLABS_API_KEY=
AZURE_TRANSLATOR_LOCATION=
AZURE_TRANSLATOR_KEY=
AZURE_RESOURCE_NAME=
AZURE_API_KEY=
MAPBOX_ACCESS_TOKEN=
FIRECRAWL_API_KEY=
TAVILY_API_KEY=tvly-**** TAVILY_API_KEY=tvly-****
OPENWEATHER_API_KEY= GROQ_API_KEY=gsk_****
E2B_API_KEY=e2b_**** OPENWEATHER_API_KEY=***
GOOGLE_MAPS_API_KEY= E2B_API_KEY=e2b_****
# Client side Env variables
NEXT_PUBLIC_POSTHOG_KEY=
NEXT_PUBLIC_POSTHOG_HOST=
NEXT_PUBLIC_MAPBOX_TOKEN=
NEXT_PUBLIC_GOOGLE_MAPS_API_KEY=

View File

@ -1,4 +1,3 @@
import { serverEnv } from '@/env/server';
import { del, list, ListBlobResult } from '@vercel/blob'; import { del, list, ListBlobResult } from '@vercel/blob';
import { NextRequest, NextResponse } from 'next/server'; import { NextRequest, NextResponse } from 'next/server';

View File

@ -1,6 +1,7 @@
import { NextResponse } from 'next/server'; import { NextResponse } from 'next/server';
import { generateObject } from 'ai'; import { generateObject } from 'ai';
import { z } from 'zod'; import { z } from 'zod';
import { geolocation } from '@vercel/functions';
import { xai } from '@ai-sdk/xai'; import { xai } from '@ai-sdk/xai';
export interface TrendingQuery { export interface TrendingQuery {
@ -15,7 +16,7 @@ interface RedditPost {
}; };
} }
async function fetchGoogleTrends(): Promise<TrendingQuery[]> { async function fetchGoogleTrends(countryCode: string = 'US'): Promise<TrendingQuery[]> {
const fetchTrends = async (geo: string): Promise<TrendingQuery[]> => { const fetchTrends = async (geo: string): Promise<TrendingQuery[]> => {
try { try {
const response = await fetch(`https://trends.google.com/trends/trendingsearches/daily/rss?geo=${geo}`, { const response = await fetch(`https://trends.google.com/trends/trendingsearches/daily/rss?geo=${geo}`, {
@ -61,7 +62,7 @@ async function fetchGoogleTrends(): Promise<TrendingQuery[]> {
} }
}; };
const trends = await fetchTrends("US"); const trends = await fetchTrends(countryCode);
return [ ...trends]; return [ ...trends];
} }
@ -94,11 +95,11 @@ async function fetchRedditQuestions(): Promise<TrendingQuery[]> {
} }
} }
async function fetchFromMultipleSources() { async function fetchFromMultipleSources(countryCode: string) {
const [googleTrends, const [googleTrends,
// redditQuestions // redditQuestions
] = await Promise.all([ ] = await Promise.all([
fetchGoogleTrends(), fetchGoogleTrends(countryCode),
// fetchRedditQuestions(), // fetchRedditQuestions(),
]); ]);
@ -111,11 +112,11 @@ async function fetchFromMultipleSources() {
export async function GET(req: Request) { export async function GET(req: Request) {
try { try {
const trends = await fetchFromMultipleSources(); const countryCode = geolocation(req).countryRegion ?? 'US';
const trends = await fetchFromMultipleSources(countryCode);
if (trends.length === 0) { if (trends.length === 0) {
// Fallback queries if both sources fail // Fallback queries if both sources fail
console.error('Both sources failed to fetch trends, returning fallback queries');
return NextResponse.json([ return NextResponse.json([
{ {
icon: 'sparkles', icon: 'sparkles',

View File

@ -1,7 +1,16 @@
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Instrument+Serif:wght@400&display=swap');
@tailwind base; @tailwind base;
@tailwind components; @tailwind components;
@tailwind utilities; @tailwind utilities;
:root {
--font-serif: "Instrument Serif", serif;
}
body {
font-family: var(--font-sans), sans-serif;
}
.homeBtn { .homeBtn {
box-shadow: rgba(0, 0, 0, 0.4) 0px 2px 4px, box-shadow: rgba(0, 0, 0, 0.4) 0px 2px 4px,
rgba(0, 0, 0, 0.3) 0px 7px 13px -3px, rgba(0, 0, 0, 0.2) 0px -3px 0px inset, rgba(0, 0, 0, 0.3) 0px 7px 13px -3px, rgba(0, 0, 0, 0.2) 0px -3px 0px inset,

View File

@ -1,13 +1,12 @@
import { Analytics } from "@vercel/analytics/react"; import "./globals.css";
import { GeistSans } from 'geist/font/sans';
import 'katex/dist/katex.min.css'; import 'katex/dist/katex.min.css';
import 'mapbox-gl/dist/mapbox-gl.css'; import 'mapbox-gl/dist/mapbox-gl.css';
import { Metadata, Viewport } from "next"; import { Metadata, Viewport } from "next";
import { Instrument_Serif } from 'next/font/google';
import { NuqsAdapter } from 'nuqs/adapters/next/app';
import { Toaster } from "sonner"; import { Toaster } from "sonner";
import "./globals.css"; import { Instrument_Serif } from 'next/font/google';
import { Providers } from './providers'; import { Analytics } from "@vercel/analytics/react";
import { Providers } from './providers'
import { GeistSans } from 'geist/font/sans';
export const metadata: Metadata = { export const metadata: Metadata = {
metadataBase: new URL("https://mplx.run"), metadataBase: new URL("https://mplx.run"),
@ -44,11 +43,7 @@ export const viewport: Viewport = {
const instrumentSerif = Instrument_Serif({ const instrumentSerif = Instrument_Serif({
weight: "400", weight: "400",
subsets: ["latin"], subsets: ["latin"],
style: ['normal', 'italic'], variable: "--font-serif"
variable: "--font-serif",
preload: true,
display: 'swap',
fallback: ['sans-serif'],
}) })
export default function RootLayout({ export default function RootLayout({
@ -58,13 +53,11 @@ export default function RootLayout({
}>) { }>) {
return ( return (
<html lang="en"> <html lang="en">
<body className={`${GeistSans.variable} ${instrumentSerif.variable} font-sans antialiased`}> <body className={` ${GeistSans.className} ${instrumentSerif.className}`}>
<NuqsAdapter> <Providers>
<Providers> <Toaster position="top-center" richColors />
<Toaster position="top-center" richColors /> {children}
{children} </Providers>
</Providers>
</NuqsAdapter>
<Analytics /> <Analytics />
</body> </body>
</html> </html>

View File

@ -2,133 +2,137 @@
"use client"; "use client";
import 'katex/dist/katex.min.css'; import 'katex/dist/katex.min.css';
import { BorderTrail } from '@/components/core/border-trail'; import
import { TextShimmer } from '@/components/core/text-shimmer'; React,
import { FlightTracker } from '@/components/flight-tracker'; {
import { InstallPrompt } from '@/components/InstallPrompt'; useRef,
useCallback,
useState,
useEffect,
useMemo,
Suspense
} from 'react';
import ReactMarkdown from 'react-markdown';
import { useTheme } from 'next-themes';
import Marked, { ReactRenderer } from 'marked-react';
import Latex from 'react-latex-next';
import { useSearchParams } from 'next/navigation';
import { useChat } from 'ai/react';
import { ToolInvocation } from 'ai';
import { toast } from 'sonner';
import { motion, AnimatePresence } from 'framer-motion';
import Image from 'next/image';
import {
fetchMetadata,
generateSpeech,
suggestQuestions
} from './actions';
import { Wave } from "@foobar404/wave";
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { oneLight, oneDark } from 'react-syntax-highlighter/dist/esm/styles/prism';
import {
Sparkles,
ArrowRight,
Globe,
AlignLeft,
Copy,
Cloud,
Code,
Check,
Loader2,
User2,
Heart,
X,
MapPin,
Plus,
Download,
Flame,
Sun,
Pause,
Play,
TrendingUpIcon,
Calendar,
Calculator,
ChevronDown,
Edit2,
ChevronUp,
Moon,
Star,
YoutubeIcon,
LucideIcon,
FileText,
Book,
ExternalLink,
Building,
Users,
Brain,
TrendingUp,
Plane,
Film,
Tv,
ListTodo
} from 'lucide-react';
import {
HoverCard,
HoverCardContent,
HoverCardTrigger,
} from "@/components/ui/hover-card";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip"
import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import {
Card,
CardContent,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from "@/components/ui/sheet";
import { Drawer, DrawerContent, DrawerHeader, DrawerTitle, DrawerTrigger } from "@/components/ui/drawer";
import { GitHubLogoIcon, TextIcon } from '@radix-ui/react-icons';
import Link from 'next/link';
import { Dialog, DialogContent } from "@/components/ui/dialog";
import { Carousel, CarouselContent, CarouselItem } from "@/components/ui/carousel";
import { cn, SearchGroupId } from '@/lib/utils';
import {
Table,
TableBody,
TableCell,
TableRow,
} from "@/components/ui/table";
import Autoplay from 'embla-carousel-autoplay';
import FormComponent from '@/components/ui/form-component';
import WeatherChart from '@/components/weather-chart';
import InteractiveChart from '@/components/interactive-charts'; import InteractiveChart from '@/components/interactive-charts';
import { MapComponent, MapContainer } from '@/components/map-components'; import { MapComponent, MapContainer } from '@/components/map-components';
import TMDBResult from '@/components/movie-info';
import MultiSearch from '@/components/multi-search'; import MultiSearch from '@/components/multi-search';
import NearbySearchMapView from '@/components/nearby-search-map-view';
import TrendingResults from '@/components/trending-tv-movies-results';
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import {
Card,
CardContent,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Carousel, CarouselContent, CarouselItem } from "@/components/ui/carousel";
import { Dialog, DialogContent } from "@/components/ui/dialog";
import { Drawer, DrawerContent, DrawerHeader, DrawerTitle, DrawerTrigger } from "@/components/ui/drawer";
import FormComponent from '@/components/ui/form-component';
import {
HoverCard,
HoverCardContent,
HoverCardTrigger,
} from "@/components/ui/hover-card";
import { Input } from '@/components/ui/input';
import { Separator } from '@/components/ui/separator';
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from "@/components/ui/sheet";
import {
Table,
TableBody,
TableCell,
TableRow,
} from "@/components/ui/table";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import WeatherChart from '@/components/weather-chart';
import { useMediaQuery } from '@/hooks/use-media-query';
import { cn, SearchGroupId } from '@/lib/utils';
import { Wave } from "@foobar404/wave";
import { CurrencyDollar, Flag, RoadHorizon, SoccerBall, TennisBall, XLogo } from '@phosphor-icons/react'; import { CurrencyDollar, Flag, RoadHorizon, SoccerBall, TennisBall, XLogo } from '@phosphor-icons/react';
import { GitHubLogoIcon, TextIcon } from '@radix-ui/react-icons'; import { BorderTrail } from '@/components/core/border-trail';
import { ToolInvocation } from 'ai'; import { TextShimmer } from '@/components/core/text-shimmer';
import { useChat } from 'ai/react';
import Autoplay from 'embla-carousel-autoplay';
import { AnimatePresence, motion } from 'framer-motion';
import { GeistMono } from 'geist/font/mono';
import {
AlignLeft,
ArrowRight,
Book,
Brain,
Building,
Calculator,
Calendar,
Check,
ChevronDown,
ChevronUp,
Cloud,
Code,
Copy,
Download,
Edit2,
ExternalLink,
FileText,
Film,
Flame,
Globe,
Heart,
ListTodo,
Loader2,
LucideIcon,
MapPin,
Moon,
Pause,
Plane,
Play,
Plus,
Sparkles,
Sun,
TrendingUp,
TrendingUpIcon,
Tv,
User2,
Users,
X,
YoutubeIcon
} from 'lucide-react';
import Marked, { ReactRenderer } from 'marked-react';
import { useTheme } from 'next-themes';
import Image from 'next/image';
import Link from 'next/link';
import { parseAsString, useQueryState } from 'nuqs';
import React, {
Suspense,
useCallback,
useEffect,
useMemo,
useRef,
useState
} from 'react';
import Latex from 'react-latex-next';
import ReactMarkdown from 'react-markdown';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { atomDark, vs } from 'react-syntax-highlighter/dist/cjs/styles/prism';
import { oneDark, oneLight } from 'react-syntax-highlighter/dist/esm/styles/prism';
import { Tweet } from 'react-tweet'; import { Tweet } from 'react-tweet';
import { toast } from 'sonner'; import NearbySearchMapView from '@/components/nearby-search-map-view';
import { import { Separator } from '@/components/ui/separator';
fetchMetadata,
generateSpeech,
suggestQuestions
} from './actions';
import { TrendingQuery } from './api/trending/route'; import { TrendingQuery } from './api/trending/route';
import { FlightTracker } from '@/components/flight-tracker';
import { InstallPrompt } from '@/components/InstallPrompt';
import { atomDark } from 'react-syntax-highlighter/dist/cjs/styles/prism';
import { vs } from 'react-syntax-highlighter/dist/cjs/styles/prism';
import { useMediaQuery } from '@/hooks/use-media-query';
import TMDBResult from '@/components/movie-info';
import TrendingResults from '@/components/trending-tv-movies-results';
import { GeistMono } from 'geist/font/mono';
export const maxDuration = 60; export const maxDuration = 60;
@ -559,15 +563,12 @@ const SponsorDialog = ({
}; };
const HomeContent = () => { const HomeContent = () => {
const [query] = useQueryState('query', parseAsString.withDefault('')) const searchParams = useSearchParams();
const [q] = useQueryState('q', parseAsString.withDefault(''))
const [model] = useQueryState('model', parseAsString.withDefault('grok-2-1212'))
// Memoize initial values to prevent re-calculation // Memoize initial values to prevent re-calculation
const initialState = useMemo(() => ({ const initialState = useMemo(() => ({
query: query || q, query: searchParams.get('query') || searchParams.get('q') || '',
model: model model: searchParams.get('model') || 'grok-2-1212'
// eslint-disable-next-line react-hooks/exhaustive-deps
}), []); // Empty dependency array as we only want this on mount }), []); // Empty dependency array as we only want this on mount
const lastSubmittedQueryRef = useRef(initialState.query); const lastSubmittedQueryRef = useRef(initialState.query);
@ -698,7 +699,6 @@ const HomeContent = () => {
}; };
fetchTrending(); fetchTrending();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
const ThemeToggle: React.FC = () => { const ThemeToggle: React.FC = () => {
@ -758,12 +758,12 @@ const HomeContent = () => {
id: "1", id: "1",
title: "The Unexpected Collab", title: "The Unexpected Collab",
images: [ images: [
"https://metwm7frkvew6tn1.public.blob.vercel-storage.com/mplx-changelogs/mplx-collab.jpeg", "https://metwm7frkvew6tn1.public.blob.vercel-storage.com/mplx-changelogs/mplx-collab.jpeg",
], ],
content: ` content: `
## **MiniPerplx x Vercel x xAI Collab** ## **MiniPerplx x Vercel x xAI Collab**
Excited to annouce that MiniPerplx has partnered with Vercel and xAI to bring you the best of AI search experience. Excited to annouce that MiniPerplx has partnered with Vercel and xAI to bring you the best of AI search experience.
Grok 2 models are now available for you to try out. Grok 2 models are now available for you to try out.
` `
} }
@ -892,11 +892,10 @@ Grok 2 models are now available for you to try out.
const waveRef = useRef<Wave | null>(null); const waveRef = useRef<Wave | null>(null);
useEffect(() => { useEffect(() => {
const _audioRef = audioRef.current
return () => { return () => {
if (_audioRef) { if (audioRef.current) {
_audioRef.pause(); audioRef.current.pause();
_audioRef.src = ''; audioRef.current.src = '';
} }
}; };
}, []); }, []);
@ -1290,7 +1289,7 @@ Grok 2 models are now available for you to try out.
return <TrendingResults result={result} type="tv" />; return <TrendingResults result={result} type="tv" />;
} }
if (toolInvocation.toolName === 'x_search') { if (toolInvocation.toolName === 'x_search') {
if (!result) { if (!result) {
return <SearchLoadingState return <SearchLoadingState
@ -2118,10 +2117,10 @@ Grok 2 models are now available for you to try out.
<button <button
onClick={handleCopy} onClick={handleCopy}
className={` className={`
px-2 py-1.5 px-2 py-1.5
rounded-md text-xs rounded-md text-xs
transition-colors duration-200 transition-colors duration-200
${isCopied ? 'bg-green-500/10 text-green-500' : 'bg-neutral-100 dark:bg-neutral-800 text-neutral-500 dark:text-neutral-400'} ${isCopied ? 'bg-green-500/10 text-green-500' : 'bg-neutral-100 dark:bg-neutral-800 text-neutral-500 dark:text-neutral-400'}
opacity-0 group-hover:opacity-100 opacity-0 group-hover:opacity-100
hover:bg-neutral-200 dark:hover:bg-neutral-700 hover:bg-neutral-200 dark:hover:bg-neutral-700
flex items-center gap-1.5 flex items-center gap-1.5
@ -2542,8 +2541,8 @@ Grok 2 models are now available for you to try out.
<button <button
key={`${index}-${query.text}`} key={`${index}-${query.text}`}
onClick={() => handleExampleClick(query)} onClick={() => handleExampleClick(query)}
className="group flex-shrink-0 bg-neutral-50/50 dark:bg-neutral-800/50 className="group flex-shrink-0 bg-neutral-50/50 dark:bg-neutral-800/50
backdrop-blur-sm rounded-xl p-3.5 text-left backdrop-blur-sm rounded-xl p-3.5 text-left
hover:bg-neutral-100 dark:hover:bg-neutral-700/70 hover:bg-neutral-100 dark:hover:bg-neutral-700/70
transition-all duration-200 ease-out transition-all duration-200 ease-out
hover:scale-102 origin-center hover:scale-102 origin-center
@ -2589,7 +2588,6 @@ Grok 2 models are now available for you to try out.
<SuggestionCards <SuggestionCards
trendingQueries={trendingQueries} trendingQueries={trendingQueries}
/> />
// eslint-disable-next-line react-hooks/exhaustive-deps
), [trendingQueries]); ), [trendingQueries]);
return ( return (

View File

@ -1,60 +0,0 @@
version: '3.8'
services:
web:
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- XAI_API_KEY=${XAI_API_KEY}
- UPSTASH_REDIS_REST_URL=${UPSTASH_REDIS_REST_URL}
- UPSTASH_REDIS_REST_TOKEN=${UPSTASH_REDIS_REST_TOKEN}
- ELEVENLABS_API_KEY=${ELEVENLABS_API_KEY}
- TAVILY_API_KEY=${TAVILY_API_KEY}
- EXA_API_KEY=${EXA_API_KEY}
- TMDB_API_KEY=${TMDB_API_KEY}
- YT_ENDPOINT=${YT_ENDPOINT}
- FIRECRAWL_API_KEY=${FIRECRAWL_API_KEY}
- OPENWEATHER_API_KEY=${OPENWEATHER_API_KEY}
- SANDBOX_TEMPLATE_ID=${SANDBOX_TEMPLATE_ID}
- GOOGLE_MAPS_API_KEY=${GOOGLE_MAPS_API_KEY}
- MAPBOX_ACCESS_TOKEN=${MAPBOX_ACCESS_TOKEN}
- AZURE_TRANSLATOR_KEY=${AZURE_TRANSLATOR_KEY}
- AZURE_TRANSLATOR_LOCATION=${AZURE_TRANSLATOR_LOCATION}
- AZURE_RESOURCE_NAME=${AZURE_RESOURCE_NAME}
- AZURE_API_KEY=${AZURE_API_KEY}
- TRIPADVISOR_API_KEY=${TRIPADVISOR_API_KEY}
- AVIATION_STACK_API_KEY=${AVIATION_STACK_API_KEY}
- CRON_SECRET=${CRON_SECRET}
- BLOB_READ_WRITE_TOKEN=${BLOB_READ_WRITE_TOKEN}
- NEXT_PUBLIC_MAPBOX_TOKEN=${NEXT_PUBLIC_MAPBOX_TOKEN}
- NEXT_PUBLIC_POSTHOG_KEY=${NEXT_PUBLIC_POSTHOG_KEY}
- NEXT_PUBLIC_POSTHOG_HOST=${NEXT_PUBLIC_POSTHOG_HOST}
- NEXT_PUBLIC_GOOGLE_MAPS_API_KEY=${NEXT_PUBLIC_GOOGLE_MAPS_API_KEY}
volumes:
- .:/app
- /app/node_modules
depends_on:
- code-interpreter
code-interpreter:
build:
context: .
dockerfile: e2b.Dockerfile
environment:
- E2B_API_KEY=${E2B_API_KEY}
volumes:
- ./certificates:/app/certificates
redis:
image: redis:alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
volumes:
redis_data:

3
env/client.ts vendored
View File

@ -7,13 +7,10 @@ export const clientEnv = createEnv({
NEXT_PUBLIC_MAPBOX_TOKEN: z.string().min(1), NEXT_PUBLIC_MAPBOX_TOKEN: z.string().min(1),
NEXT_PUBLIC_POSTHOG_KEY: z.string().min(1), NEXT_PUBLIC_POSTHOG_KEY: z.string().min(1),
NEXT_PUBLIC_POSTHOG_HOST: z.string().min(1).url(), NEXT_PUBLIC_POSTHOG_HOST: z.string().min(1).url(),
NEXT_PUBLIC_GOOGLE_MAPS_API_KEY: z.string().min(1),
}, },
runtimeEnv: { runtimeEnv: {
NEXT_PUBLIC_MAPBOX_TOKEN: process.env.NEXT_PUBLIC_MAPBOX_TOKEN, NEXT_PUBLIC_MAPBOX_TOKEN: process.env.NEXT_PUBLIC_MAPBOX_TOKEN,
NEXT_PUBLIC_POSTHOG_KEY: process.env.NEXT_PUBLIC_POSTHOG_KEY, NEXT_PUBLIC_POSTHOG_KEY: process.env.NEXT_PUBLIC_POSTHOG_KEY,
NEXT_PUBLIC_POSTHOG_HOST: process.env.NEXT_PUBLIC_POSTHOG_HOST, NEXT_PUBLIC_POSTHOG_HOST: process.env.NEXT_PUBLIC_POSTHOG_HOST,
NEXT_PUBLIC_GOOGLE_MAPS_API_KEY: process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY
}, },
}) })

6
env/server.ts vendored
View File

@ -4,9 +4,6 @@ import { z } from 'zod'
export const serverEnv = createEnv({ export const serverEnv = createEnv({
server: { server: {
XAI_API_KEY: z.string().min(1),
UPSTASH_REDIS_REST_URL: z.string().min(1).url(),
UPSTASH_REDIS_REST_TOKEN: z.string().min(1),
ELEVENLABS_API_KEY: z.string().min(1), ELEVENLABS_API_KEY: z.string().min(1),
TAVILY_API_KEY: z.string().min(1), TAVILY_API_KEY: z.string().min(1),
EXA_API_KEY: z.string().min(1), EXA_API_KEY: z.string().min(1),
@ -19,12 +16,9 @@ export const serverEnv = createEnv({
MAPBOX_ACCESS_TOKEN: z.string().min(1), MAPBOX_ACCESS_TOKEN: z.string().min(1),
AZURE_TRANSLATOR_KEY: z.string().min(1), AZURE_TRANSLATOR_KEY: z.string().min(1),
AZURE_TRANSLATOR_LOCATION: z.string().min(1), AZURE_TRANSLATOR_LOCATION: z.string().min(1),
AZURE_RESOURCE_NAME: z.string().min(1),
AZURE_API_KEY: z.string().min(1),
TRIPADVISOR_API_KEY: z.string().min(1), TRIPADVISOR_API_KEY: z.string().min(1),
AVIATION_STACK_API_KEY: z.string().min(1), AVIATION_STACK_API_KEY: z.string().min(1),
CRON_SECRET: z.string().min(1), CRON_SECRET: z.string().min(1),
BLOB_READ_WRITE_TOKEN: z.string().min(1),
}, },
experimental__runtimeEnv: process.env, experimental__runtimeEnv: process.env,
}) })

View File

@ -9,86 +9,86 @@ jiti.import('./env/client')
/** @type {import('next').NextConfig} */ /** @type {import('next').NextConfig} */
const nextConfig = { const nextConfig = {
transpilePackages: ["geist"], transpilePackages: ['geist'],
async headers() { async headers() {
return [ return [
{ {
source: '/(.*)', source: '/(.*)',
headers: [ headers: [
{ {
key: 'X-Content-Type-Options', key: 'X-Content-Type-Options',
value: 'nosniff', value: 'nosniff',
}, },
{ {
key: 'X-Frame-Options', key: 'X-Frame-Options',
value: 'DENY', value: 'DENY',
}, },
{ {
key: 'Referrer-Policy', key: 'Referrer-Policy',
value: 'strict-origin-when-cross-origin', value: 'strict-origin-when-cross-origin',
}, },
], ],
}, },
] ]
}, },
images: { images: {
dangerouslyAllowSVG: true, dangerouslyAllowSVG: true,
remotePatterns: [ remotePatterns: [
{ {
protocol: 'https', protocol: 'https',
hostname: 'www.google.com', hostname: 'www.google.com',
port: '', port: '',
pathname: '/s2/favicons', pathname: '/s2/favicons',
}, },
{ {
protocol: 'https', protocol: 'https',
hostname: 'api.producthunt.com', hostname: 'api.producthunt.com',
port: '', port: '',
pathname: '/widgets/embed-image/v1/featured.svg', pathname: '/widgets/embed-image/v1/featured.svg',
}, },
{ {
protocol: 'https', protocol: 'https',
hostname: 'metwm7frkvew6tn1.public.blob.vercel-storage.com', hostname: 'metwm7frkvew6tn1.public.blob.vercel-storage.com',
port: '', port: '',
pathname: "**" pathname: '**',
}, },
// upload.wikimedia.org // upload.wikimedia.org
{ {
protocol: 'https', protocol: 'https',
hostname: 'upload.wikimedia.org', hostname: 'upload.wikimedia.org',
port: '', port: '',
pathname: '**' pathname: '**',
}, },
// media.theresanaiforthat.com // media.theresanaiforthat.com
{ {
protocol: 'https', protocol: 'https',
hostname: 'media.theresanaiforthat.com', hostname: 'media.theresanaiforthat.com',
port: '', port: '',
pathname: '**' pathname: '**',
}, },
// www.uneed.best // www.uneed.best
{ {
protocol: 'https', protocol: 'https',
hostname: 'www.uneed.best', hostname: 'www.uneed.best',
port: '', port: '',
pathname: '**' pathname: '**',
}, },
// image.tmdb.org // image.tmdb.org
{ {
protocol: 'https', protocol: 'https',
hostname: 'image.tmdb.org', hostname: 'image.tmdb.org',
port: '', port: '',
pathname: '/t/p/original/**' pathname: '/t/p/original/**',
}, },
// image.tmdb.org // image.tmdb.org
{ {
protocol: 'https', protocol: 'https',
hostname: 'image.tmdb.org', hostname: 'image.tmdb.org',
port: '', port: '',
pathname: '/**' pathname: '/**',
}, },
] ],
}, },
}; }
export default nextConfig; export default nextConfig

View File

@ -64,7 +64,6 @@
"motion": "^11.13.5", "motion": "^11.13.5",
"next": "^14.2.21", "next": "^14.2.21",
"next-themes": "^0.3.0", "next-themes": "^0.3.0",
"nuqs": "^2.3.0",
"openai": "^4.56.0", "openai": "^4.56.0",
"posthog-js": "^1.202.2", "posthog-js": "^1.202.2",
"react": "^18", "react": "^18",

View File

@ -173,9 +173,6 @@ importers:
next-themes: next-themes:
specifier: ^0.3.0 specifier: ^0.3.0
version: 0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) version: 0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
nuqs:
specifier: ^2.3.0
version: 2.3.0(next@14.2.21(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)
openai: openai:
specifier: ^4.56.0 specifier: ^4.56.0
version: 4.67.2(zod@3.24.1) version: 4.67.2(zod@3.24.1)
@ -2789,9 +2786,6 @@ packages:
resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
engines: {node: '>=16 || 14 >=14.17'} engines: {node: '>=16 || 14 >=14.17'}
mitt@3.0.1:
resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
motion-dom@11.13.0: motion-dom@11.13.0:
resolution: {integrity: sha512-Oc1MLGJQ6nrvXccXA89lXtOqFyBmvHtaDcTRGT66o8Czl7nuA8BeHAd9MQV1pQKX0d2RHFBFaw5g3k23hQJt0w==} resolution: {integrity: sha512-Oc1MLGJQ6nrvXccXA89lXtOqFyBmvHtaDcTRGT66o8Czl7nuA8BeHAd9MQV1pQKX0d2RHFBFaw5g3k23hQJt0w==}
@ -2883,24 +2877,6 @@ packages:
nth-check@2.1.1: nth-check@2.1.1:
resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
nuqs@2.3.0:
resolution: {integrity: sha512-ChS56bJZdaTQzCJb6jPel6cIHYh8/V/GSIjZoIe5yAssGdcrVaBFBgzHfJW6IewbR6yc1Zch2CmGsdgztR+xmA==}
peerDependencies:
'@remix-run/react': '>=2'
next: '>=14.2.0'
react: '>=18.2.0 || ^19.0.0-0'
react-router: ^7
react-router-dom: ^6 || ^7
peerDependenciesMeta:
'@remix-run/react':
optional: true
next:
optional: true
react-router:
optional: true
react-router-dom:
optional: true
object-assign@4.1.1: object-assign@4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@ -6759,8 +6735,6 @@ snapshots:
minipass@7.1.2: {} minipass@7.1.2: {}
mitt@3.0.1: {}
motion-dom@11.13.0: {} motion-dom@11.13.0: {}
motion-utils@11.13.0: {} motion-utils@11.13.0: {}
@ -6834,13 +6808,6 @@ snapshots:
dependencies: dependencies:
boolbase: 1.0.0 boolbase: 1.0.0
nuqs@2.3.0(next@14.2.21(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1):
dependencies:
mitt: 3.0.1
react: 18.3.1
optionalDependencies:
next: 14.2.21(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
object-assign@4.1.1: {} object-assign@4.1.1: {}
object-hash@3.0.0: {} object-hash@3.0.0: {}

View File

@ -1,5 +1,4 @@
import type { Config } from "tailwindcss"; import type { Config } from "tailwindcss";
import { fontFamily } from 'tailwindcss/defaultTheme';
const config = { const config = {
darkMode: ["class"], darkMode: ["class"],
@ -24,9 +23,9 @@ const config = {
'screen-small': '100svh', 'screen-small': '100svh',
}, },
fontFamily: { fontFamily: {
sans: ['var(--font-geist-sans)', ...fontFamily.sans], sans: ['var(--font-geist-sans)'],
serif: ['var(--font-serif)', ...fontFamily.serif], serif: ['var(--font-serif)'],
mono: ['var(--font-geist-mono)', ...fontFamily.mono], mono: ['var(--font-geist-mono)'],
}, },
colors: { colors: {
border: "hsl(var(--border))", border: "hsl(var(--border))",
@ -88,11 +87,7 @@ const config = {
}, },
}, },
}, },
plugins: [ plugins: [require("tailwindcss-animate"), require("@tailwindcss/typography"),require("tailwind-scrollbar")],
require("tailwindcss-animate"),
require("@tailwindcss/typography"),
require("tailwind-scrollbar")
],
} satisfies Config } satisfies Config
export default config export default config