Compare commits
10 Commits
186bd3c2cc
...
d468ecca74
| Author | SHA1 | Date | |
|---|---|---|---|
| d468ecca74 | |||
|
|
15d5ac5a61 | ||
|
|
706f53f3ed | ||
|
|
8247f9b4ea | ||
|
|
527e46453d | ||
|
|
0289fc44df | ||
|
|
690fd9fc79 | ||
|
|
74d4d8d5a0 | ||
|
|
fba1ccb0ef | ||
|
|
f8ce4bd871 |
29
.env.example
29
.env.example
@ -1,5 +1,28 @@
|
||||
ANTHROPIC_API_KEY=sk-ant-api****
|
||||
# Strictly Server side Env variables
|
||||
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-****
|
||||
GROQ_API_KEY=gsk_****
|
||||
OPENWEATHER_API_KEY=***
|
||||
OPENWEATHER_API_KEY=
|
||||
E2B_API_KEY=e2b_****
|
||||
GOOGLE_MAPS_API_KEY=
|
||||
|
||||
# Client side Env variables
|
||||
NEXT_PUBLIC_POSTHOG_KEY=
|
||||
NEXT_PUBLIC_POSTHOG_HOST=
|
||||
NEXT_PUBLIC_MAPBOX_TOKEN=
|
||||
NEXT_PUBLIC_GOOGLE_MAPS_API_KEY=
|
||||
@ -1,3 +1,4 @@
|
||||
import { serverEnv } from '@/env/server';
|
||||
import { del, list, ListBlobResult } from '@vercel/blob';
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { generateObject } from 'ai';
|
||||
import { z } from 'zod';
|
||||
import { geolocation } from '@vercel/functions';
|
||||
import { xai } from '@ai-sdk/xai';
|
||||
|
||||
export interface TrendingQuery {
|
||||
@ -16,7 +15,7 @@ interface RedditPost {
|
||||
};
|
||||
}
|
||||
|
||||
async function fetchGoogleTrends(countryCode: string = 'US'): Promise<TrendingQuery[]> {
|
||||
async function fetchGoogleTrends(): Promise<TrendingQuery[]> {
|
||||
const fetchTrends = async (geo: string): Promise<TrendingQuery[]> => {
|
||||
try {
|
||||
const response = await fetch(`https://trends.google.com/trends/trendingsearches/daily/rss?geo=${geo}`, {
|
||||
@ -62,7 +61,7 @@ async function fetchGoogleTrends(countryCode: string = 'US'): Promise<TrendingQu
|
||||
}
|
||||
};
|
||||
|
||||
const trends = await fetchTrends(countryCode);
|
||||
const trends = await fetchTrends("US");
|
||||
|
||||
return [ ...trends];
|
||||
}
|
||||
@ -95,11 +94,11 @@ async function fetchRedditQuestions(): Promise<TrendingQuery[]> {
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchFromMultipleSources(countryCode: string) {
|
||||
async function fetchFromMultipleSources() {
|
||||
const [googleTrends,
|
||||
// redditQuestions
|
||||
] = await Promise.all([
|
||||
fetchGoogleTrends(countryCode),
|
||||
fetchGoogleTrends(),
|
||||
// fetchRedditQuestions(),
|
||||
]);
|
||||
|
||||
@ -112,11 +111,11 @@ async function fetchFromMultipleSources(countryCode: string) {
|
||||
|
||||
export async function GET(req: Request) {
|
||||
try {
|
||||
const countryCode = geolocation(req).countryRegion ?? 'US';
|
||||
const trends = await fetchFromMultipleSources(countryCode);
|
||||
const trends = await fetchFromMultipleSources();
|
||||
|
||||
if (trends.length === 0) {
|
||||
// Fallback queries if both sources fail
|
||||
console.error('Both sources failed to fetch trends, returning fallback queries');
|
||||
return NextResponse.json([
|
||||
{
|
||||
icon: 'sparkles',
|
||||
|
||||
@ -1,16 +1,7 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Instrument+Serif:wght@400&display=swap');
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
:root {
|
||||
--font-serif: "Instrument Serif", serif;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--font-sans), sans-serif;
|
||||
}
|
||||
|
||||
.homeBtn {
|
||||
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,
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
import "./globals.css";
|
||||
import { Analytics } from "@vercel/analytics/react";
|
||||
import { GeistSans } from 'geist/font/sans';
|
||||
import 'katex/dist/katex.min.css';
|
||||
import 'mapbox-gl/dist/mapbox-gl.css';
|
||||
import { Metadata, Viewport } from "next";
|
||||
import { Toaster } from "sonner";
|
||||
import { Instrument_Serif } from 'next/font/google';
|
||||
import { Analytics } from "@vercel/analytics/react";
|
||||
import { Providers } from './providers'
|
||||
import { GeistSans } from 'geist/font/sans';
|
||||
import { NuqsAdapter } from 'nuqs/adapters/next/app';
|
||||
import { Toaster } from "sonner";
|
||||
import "./globals.css";
|
||||
import { Providers } from './providers';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
metadataBase: new URL("https://mplx.run"),
|
||||
@ -43,7 +44,11 @@ export const viewport: Viewport = {
|
||||
const instrumentSerif = Instrument_Serif({
|
||||
weight: "400",
|
||||
subsets: ["latin"],
|
||||
variable: "--font-serif"
|
||||
style: ['normal', 'italic'],
|
||||
variable: "--font-serif",
|
||||
preload: true,
|
||||
display: 'swap',
|
||||
fallback: ['sans-serif'],
|
||||
})
|
||||
|
||||
export default function RootLayout({
|
||||
@ -53,11 +58,13 @@ export default function RootLayout({
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={` ${GeistSans.className} ${instrumentSerif.className}`}>
|
||||
<Providers>
|
||||
<Toaster position="top-center" richColors />
|
||||
{children}
|
||||
</Providers>
|
||||
<body className={`${GeistSans.variable} ${instrumentSerif.variable} font-sans antialiased`}>
|
||||
<NuqsAdapter>
|
||||
<Providers>
|
||||
<Toaster position="top-center" richColors />
|
||||
{children}
|
||||
</Providers>
|
||||
</NuqsAdapter>
|
||||
<Analytics />
|
||||
</body>
|
||||
</html>
|
||||
|
||||
262
app/page.tsx
262
app/page.tsx
@ -2,137 +2,133 @@
|
||||
"use client";
|
||||
import 'katex/dist/katex.min.css';
|
||||
|
||||
import
|
||||
React,
|
||||
{
|
||||
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 { MapComponent, MapContainer } from '@/components/map-components';
|
||||
import MultiSearch from '@/components/multi-search';
|
||||
import { CurrencyDollar, Flag, RoadHorizon, SoccerBall, TennisBall, XLogo } from '@phosphor-icons/react';
|
||||
import { BorderTrail } from '@/components/core/border-trail';
|
||||
import { TextShimmer } from '@/components/core/text-shimmer';
|
||||
import { Tweet } from 'react-tweet';
|
||||
import NearbySearchMapView from '@/components/nearby-search-map-view';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
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 InteractiveChart from '@/components/interactive-charts';
|
||||
import { MapComponent, MapContainer } from '@/components/map-components';
|
||||
import TMDBResult from '@/components/movie-info';
|
||||
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 { GitHubLogoIcon, TextIcon } from '@radix-ui/react-icons';
|
||||
import { ToolInvocation } from 'ai';
|
||||
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 { toast } from 'sonner';
|
||||
import {
|
||||
fetchMetadata,
|
||||
generateSpeech,
|
||||
suggestQuestions
|
||||
} from './actions';
|
||||
import { TrendingQuery } from './api/trending/route';
|
||||
|
||||
export const maxDuration = 60;
|
||||
|
||||
@ -563,12 +559,15 @@ const SponsorDialog = ({
|
||||
};
|
||||
|
||||
const HomeContent = () => {
|
||||
const searchParams = useSearchParams();
|
||||
const [query] = useQueryState('query', parseAsString.withDefault(''))
|
||||
const [q] = useQueryState('q', parseAsString.withDefault(''))
|
||||
const [model] = useQueryState('model', parseAsString.withDefault('grok-2-1212'))
|
||||
|
||||
// Memoize initial values to prevent re-calculation
|
||||
const initialState = useMemo(() => ({
|
||||
query: searchParams.get('query') || searchParams.get('q') || '',
|
||||
model: searchParams.get('model') || 'grok-2-1212'
|
||||
query: query || q,
|
||||
model: model
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}), []); // Empty dependency array as we only want this on mount
|
||||
|
||||
const lastSubmittedQueryRef = useRef(initialState.query);
|
||||
@ -699,6 +698,7 @@ const HomeContent = () => {
|
||||
};
|
||||
|
||||
fetchTrending();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const ThemeToggle: React.FC = () => {
|
||||
@ -892,10 +892,11 @@ Grok 2 models are now available for you to try out.
|
||||
const waveRef = useRef<Wave | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const _audioRef = audioRef.current
|
||||
return () => {
|
||||
if (audioRef.current) {
|
||||
audioRef.current.pause();
|
||||
audioRef.current.src = '';
|
||||
if (_audioRef) {
|
||||
_audioRef.pause();
|
||||
_audioRef.src = '';
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
@ -2588,6 +2589,7 @@ Grok 2 models are now available for you to try out.
|
||||
<SuggestionCards
|
||||
trendingQueries={trendingQueries}
|
||||
/>
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
), [trendingQueries]);
|
||||
|
||||
return (
|
||||
|
||||
60
docker-compose.yaml
Normal file
60
docker-compose.yaml
Normal file
@ -0,0 +1,60 @@
|
||||
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
3
env/client.ts
vendored
@ -7,10 +7,13 @@ export const clientEnv = createEnv({
|
||||
NEXT_PUBLIC_MAPBOX_TOKEN: z.string().min(1),
|
||||
NEXT_PUBLIC_POSTHOG_KEY: z.string().min(1),
|
||||
NEXT_PUBLIC_POSTHOG_HOST: z.string().min(1).url(),
|
||||
NEXT_PUBLIC_GOOGLE_MAPS_API_KEY: z.string().min(1),
|
||||
},
|
||||
runtimeEnv: {
|
||||
NEXT_PUBLIC_MAPBOX_TOKEN: process.env.NEXT_PUBLIC_MAPBOX_TOKEN,
|
||||
NEXT_PUBLIC_POSTHOG_KEY: process.env.NEXT_PUBLIC_POSTHOG_KEY,
|
||||
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
6
env/server.ts
vendored
@ -4,6 +4,9 @@ import { z } from 'zod'
|
||||
|
||||
export const serverEnv = createEnv({
|
||||
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),
|
||||
TAVILY_API_KEY: z.string().min(1),
|
||||
EXA_API_KEY: z.string().min(1),
|
||||
@ -16,9 +19,12 @@ export const serverEnv = createEnv({
|
||||
MAPBOX_ACCESS_TOKEN: z.string().min(1),
|
||||
AZURE_TRANSLATOR_KEY: 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),
|
||||
AVIATION_STACK_API_KEY: z.string().min(1),
|
||||
CRON_SECRET: z.string().min(1),
|
||||
BLOB_READ_WRITE_TOKEN: z.string().min(1),
|
||||
},
|
||||
experimental__runtimeEnv: process.env,
|
||||
})
|
||||
|
||||
164
next.config.mjs
164
next.config.mjs
@ -9,86 +9,86 @@ jiti.import('./env/client')
|
||||
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
transpilePackages: ['geist'],
|
||||
async headers() {
|
||||
return [
|
||||
{
|
||||
source: '/(.*)',
|
||||
headers: [
|
||||
{
|
||||
key: 'X-Content-Type-Options',
|
||||
value: 'nosniff',
|
||||
},
|
||||
{
|
||||
key: 'X-Frame-Options',
|
||||
value: 'DENY',
|
||||
},
|
||||
{
|
||||
key: 'Referrer-Policy',
|
||||
value: 'strict-origin-when-cross-origin',
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
},
|
||||
images: {
|
||||
dangerouslyAllowSVG: true,
|
||||
remotePatterns: [
|
||||
{
|
||||
protocol: 'https',
|
||||
hostname: 'www.google.com',
|
||||
port: '',
|
||||
pathname: '/s2/favicons',
|
||||
},
|
||||
{
|
||||
protocol: 'https',
|
||||
hostname: 'api.producthunt.com',
|
||||
port: '',
|
||||
pathname: '/widgets/embed-image/v1/featured.svg',
|
||||
},
|
||||
{
|
||||
protocol: 'https',
|
||||
hostname: 'metwm7frkvew6tn1.public.blob.vercel-storage.com',
|
||||
port: '',
|
||||
pathname: '**',
|
||||
},
|
||||
// upload.wikimedia.org
|
||||
{
|
||||
protocol: 'https',
|
||||
hostname: 'upload.wikimedia.org',
|
||||
port: '',
|
||||
pathname: '**',
|
||||
},
|
||||
// media.theresanaiforthat.com
|
||||
{
|
||||
protocol: 'https',
|
||||
hostname: 'media.theresanaiforthat.com',
|
||||
port: '',
|
||||
pathname: '**',
|
||||
},
|
||||
// www.uneed.best
|
||||
{
|
||||
protocol: 'https',
|
||||
hostname: 'www.uneed.best',
|
||||
port: '',
|
||||
pathname: '**',
|
||||
},
|
||||
// image.tmdb.org
|
||||
{
|
||||
protocol: 'https',
|
||||
hostname: 'image.tmdb.org',
|
||||
port: '',
|
||||
pathname: '/t/p/original/**',
|
||||
},
|
||||
// image.tmdb.org
|
||||
{
|
||||
protocol: 'https',
|
||||
hostname: 'image.tmdb.org',
|
||||
port: '',
|
||||
pathname: '/**',
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
transpilePackages: ["geist"],
|
||||
async headers() {
|
||||
return [
|
||||
{
|
||||
source: '/(.*)',
|
||||
headers: [
|
||||
{
|
||||
key: 'X-Content-Type-Options',
|
||||
value: 'nosniff',
|
||||
},
|
||||
{
|
||||
key: 'X-Frame-Options',
|
||||
value: 'DENY',
|
||||
},
|
||||
{
|
||||
key: 'Referrer-Policy',
|
||||
value: 'strict-origin-when-cross-origin',
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
},
|
||||
images: {
|
||||
dangerouslyAllowSVG: true,
|
||||
remotePatterns: [
|
||||
{
|
||||
protocol: 'https',
|
||||
hostname: 'www.google.com',
|
||||
port: '',
|
||||
pathname: '/s2/favicons',
|
||||
},
|
||||
{
|
||||
protocol: 'https',
|
||||
hostname: 'api.producthunt.com',
|
||||
port: '',
|
||||
pathname: '/widgets/embed-image/v1/featured.svg',
|
||||
},
|
||||
{
|
||||
protocol: 'https',
|
||||
hostname: 'metwm7frkvew6tn1.public.blob.vercel-storage.com',
|
||||
port: '',
|
||||
pathname: "**"
|
||||
},
|
||||
// upload.wikimedia.org
|
||||
{
|
||||
protocol: 'https',
|
||||
hostname: 'upload.wikimedia.org',
|
||||
port: '',
|
||||
pathname: '**'
|
||||
},
|
||||
// media.theresanaiforthat.com
|
||||
{
|
||||
protocol: 'https',
|
||||
hostname: 'media.theresanaiforthat.com',
|
||||
port: '',
|
||||
pathname: '**'
|
||||
},
|
||||
// www.uneed.best
|
||||
{
|
||||
protocol: 'https',
|
||||
hostname: 'www.uneed.best',
|
||||
port: '',
|
||||
pathname: '**'
|
||||
},
|
||||
// image.tmdb.org
|
||||
{
|
||||
protocol: 'https',
|
||||
hostname: 'image.tmdb.org',
|
||||
port: '',
|
||||
pathname: '/t/p/original/**'
|
||||
},
|
||||
// image.tmdb.org
|
||||
{
|
||||
protocol: 'https',
|
||||
hostname: 'image.tmdb.org',
|
||||
port: '',
|
||||
pathname: '/**'
|
||||
},
|
||||
]
|
||||
},
|
||||
};
|
||||
|
||||
export default nextConfig
|
||||
export default nextConfig;
|
||||
|
||||
@ -64,6 +64,7 @@
|
||||
"motion": "^11.13.5",
|
||||
"next": "^14.2.21",
|
||||
"next-themes": "^0.3.0",
|
||||
"nuqs": "^2.3.0",
|
||||
"openai": "^4.56.0",
|
||||
"posthog-js": "^1.202.2",
|
||||
"react": "^18",
|
||||
|
||||
@ -173,6 +173,9 @@ importers:
|
||||
next-themes:
|
||||
specifier: ^0.3.0
|
||||
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:
|
||||
specifier: ^4.56.0
|
||||
version: 4.67.2(zod@3.24.1)
|
||||
@ -2786,6 +2789,9 @@ packages:
|
||||
resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
|
||||
engines: {node: '>=16 || 14 >=14.17'}
|
||||
|
||||
mitt@3.0.1:
|
||||
resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
|
||||
|
||||
motion-dom@11.13.0:
|
||||
resolution: {integrity: sha512-Oc1MLGJQ6nrvXccXA89lXtOqFyBmvHtaDcTRGT66o8Czl7nuA8BeHAd9MQV1pQKX0d2RHFBFaw5g3k23hQJt0w==}
|
||||
|
||||
@ -2877,6 +2883,24 @@ packages:
|
||||
nth-check@2.1.1:
|
||||
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:
|
||||
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@ -6735,6 +6759,8 @@ snapshots:
|
||||
|
||||
minipass@7.1.2: {}
|
||||
|
||||
mitt@3.0.1: {}
|
||||
|
||||
motion-dom@11.13.0: {}
|
||||
|
||||
motion-utils@11.13.0: {}
|
||||
@ -6808,6 +6834,13 @@ snapshots:
|
||||
dependencies:
|
||||
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-hash@3.0.0: {}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { Config } from "tailwindcss";
|
||||
import { fontFamily } from 'tailwindcss/defaultTheme';
|
||||
|
||||
const config = {
|
||||
darkMode: ["class"],
|
||||
@ -23,9 +24,9 @@ const config = {
|
||||
'screen-small': '100svh',
|
||||
},
|
||||
fontFamily: {
|
||||
sans: ['var(--font-geist-sans)'],
|
||||
serif: ['var(--font-serif)'],
|
||||
mono: ['var(--font-geist-mono)'],
|
||||
sans: ['var(--font-geist-sans)', ...fontFamily.sans],
|
||||
serif: ['var(--font-serif)', ...fontFamily.serif],
|
||||
mono: ['var(--font-geist-mono)', ...fontFamily.mono],
|
||||
},
|
||||
colors: {
|
||||
border: "hsl(var(--border))",
|
||||
@ -87,7 +88,11 @@ const config = {
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [require("tailwindcss-animate"), require("@tailwindcss/typography"),require("tailwind-scrollbar")],
|
||||
plugins: [
|
||||
require("tailwindcss-animate"),
|
||||
require("@tailwindcss/typography"),
|
||||
require("tailwind-scrollbar")
|
||||
],
|
||||
} satisfies Config
|
||||
|
||||
export default config
|
||||
Loading…
Reference in New Issue
Block a user