Skip to content

Upgrade to Next.js 15 and React 19. #67

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@
# For detailed instructions on using these variables, visit:
# https://github.com/blrchen/chatgpt-lite#environment-variables

# -----------------------------------------------------------------------------
# OpenAI API credentials (required for using the OpenAI API):
# Sign up at https://openai.com/api/ to get your API key.
# -----------------------------------------------------------------------------
OPENAI_API_KEY= # <-- Insert your OpenAI API key here
OPENAI_API_BASE_URL="https://api.openai.com"

# -----------------------------------------------------------------------------
# Azure OpenAI API credentials (required for using the Azure OpenAI API):
# Sign up at https://azure.microsoft.com/en-us/services/cognitive-services/openai/ for Azure OpenAI credentials.
# Uncomment the following lines and insert your credentials if you're using Azure OpenAI.
# -----------------------------------------------------------------------------
# AZURE_OPENAI_API_BASE_URL= # <-- Insert your Azure OpenAI API base URL here
# AZURE_OPENAI_API_KEY= # <-- Insert your Azure OpenAI API key here
# AZURE_OPENAI_DEPLOYMENT= # <-- Insert your Azure OpenAI deployment name here

46 changes: 0 additions & 46 deletions .eslintrc.json

This file was deleted.

4 changes: 1 addition & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz

# testing
/coverage
Expand Down Expand Up @@ -33,6 +34,3 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts

# JetBrains
.idea/
4 changes: 3 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
.next
node_modules
node_modules
package.json
package-lock.json
8 changes: 4 additions & 4 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
{
"type": "node",
"request": "launch",
"name": "website",
"cwd": "${workspaceFolder}",
"name": "Launch Program",
"skipFiles": ["<node_internals>/**"],
"runtimeExecutable": "npm",
"runtimeArgs": ["run", "dev"],
"cwd": "${workspaceFolder}",
"autoAttachChildProcesses": true,
"stopOnEntry": true,
"skipFiles": ["<node_internals>/**"]
"stopOnEntry": true
}
]
}
16 changes: 14 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
{
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll": "explicit"
"source.fixAll.eslint": "explicit"
},
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"eslint.workingDirectories": [
{
"mode": "auto"
}
],
"[css]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
32 changes: 13 additions & 19 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,51 +1,45 @@
# This Dockerfile is generated based on sample in the following document
# https://github.com/vercel/next.js/blob/canary/examples/with-docker/Dockerfile

FROM node:20-alpine AS base

# Install dependencies only when needed
FROM base AS deps
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat

WORKDIR /app

# Install dependencies based on the preferred package manager
COPY package.json package-lock.json* ./
RUN npm ci

# Rebuild the source code only when needed
FROM base AS builder
FROM deps AS builder

WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules

# Install dependencies based on the preferred package manager
COPY . .
RUN npm run build

# Production image, copy all the files and run next
FROM base AS runner
WORKDIR /app

ENV NODE_ENV production

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
RUN addgroup --system --gid 1001 nodejs && \
adduser --system --uid 1001 nextjs && \
mkdir .next && \
chown nextjs:nodejs .next

COPY --from=builder /app/public ./public

# Set the correct permission for prerender cache
RUN mkdir .next
RUN chown nextjs:nodejs .next

# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs

EXPOSE 3000

ENV NODE_ENV production

ENV PORT 3000
ENV HOSTNAME "0.0.0.0"

# server.js is created by next build from the standalone output
# https://nextjs.org/docs/pages/api-reference/next-config-js/output
CMD HOSTNAME="0.0.0.0" node server.js
CMD ["node", "server.js"]
4 changes: 1 addition & 3 deletions app/chat/PersonaPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@ import { AiOutlineClose, AiOutlineDelete, AiOutlineEdit } from 'react-icons/ai'
import { LuMessageSquarePlus } from 'react-icons/lu'
import { ChatContext, Persona } from '@/components'

export interface PersonaPanelProps {}

const PersonaPanel = (_props: PersonaPanelProps) => {
const PersonaPanel = () => {
const {
personaPanelType,
DefaultPersonas,
Expand Down
10 changes: 6 additions & 4 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { Analytics } from '@vercel/analytics/react'
import { Toaster } from 'react-hot-toast'
import { Metadata } from 'next'
import { Toaster } from 'sonner'
import { Header } from '@/components/Header'
import ThemesProvider from '@/providers/ThemesProvider'

import '@/styles/globals.scss'
import '@/styles/theme-config.css'

export const metadata = {
export const metadata: Metadata = {
title: {
default: 'ChatGPT Lite',
template: `%s - ChatGPT Lite`
Expand All @@ -20,12 +22,12 @@ export const metadata = {

export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<html lang="en" className="light" style={{ colorScheme: 'light' }}>
<body>
<ThemesProvider>
<Header />
{children}
<Toaster />
<Toaster position="top-center" toastOptions={{ style: { color: 'red' } }} />
</ThemesProvider>
<Analytics />
</body>
Expand Down
69 changes: 34 additions & 35 deletions components/Chat/Chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,16 @@ import {
} from 'react'
import { Flex, Heading, IconButton, ScrollArea, Tooltip } from '@radix-ui/themes'
import ContentEditable from 'react-contenteditable'
import toast from 'react-hot-toast'
import { AiOutlineClear, AiOutlineLoading3Quarters, AiOutlineUnorderedList } from 'react-icons/ai'
import { FiSend } from 'react-icons/fi'
import sanitizeHtml from 'sanitize-html'
import { toast } from 'sonner'
import ChatContext from './chatContext'
import type { Chat, ChatMessage } from './interface'
import Message from './Message'

import './index.scss'

const HTML_REGULAR =
/<(?!img|table|\/table|thead|\/thead|tbody|\/tbody|tr|\/tr|td|\/td|th|\/th|br|\/br).*?>/gi

export interface ChatProps {}

export interface ChatGPInstance {
Expand Down Expand Up @@ -70,8 +68,7 @@ const Chat = (props: ChatProps, ref: any) => {
async (e: any) => {
if (!isLoading) {
e.preventDefault()
const input = textAreaRef.current?.innerHTML?.replace(HTML_REGULAR, '') || ''

const input = sanitizeHtml(textAreaRef.current?.innerHTML || '')
if (input.length < 1) {
toast.error('Please type a message to continue.')
return
Expand Down Expand Up @@ -214,7 +211,33 @@ const Chat = (props: ChatProps, ref: any) => {
px="4"
style={{ backgroundColor: 'var(--gray-a2)' }}
>
<Heading size="4">{currentChatRef?.current?.persona?.name || 'None'}</Heading>
<Heading size="4">{currentChatRef?.current?.persona?.name || 'No Persona'}</Heading>
<Flex gap="2" align="center">
<Tooltip content="Clear History">
<IconButton
variant="soft"
color="gray"
size="2"
className="rounded-xl cursor-pointer"
disabled={isLoading}
onClick={clearMessages}
>
<AiOutlineClear className="size-5" />
</IconButton>
</Tooltip>
<Tooltip content="Toggle Sidebar">
<IconButton
variant="soft"
color="gray"
size="2"
className="rounded-lg md:hidden cursor-pointer"
disabled={isLoading}
onClick={onToggleSidebar}
>
<AiOutlineUnorderedList className="size-5" />
</IconButton>
</Tooltip>
</Flex>
</Flex>
<ScrollArea
className="flex-1 px-4"
Expand Down Expand Up @@ -242,7 +265,7 @@ const Chat = (props: ChatProps, ref: any) => {
html={message}
disabled={isLoading}
onChange={(e) => {
setMessage(e.target.value.replace(HTML_REGULAR, ''))
setMessage(sanitizeHtml(e.target.value))
}}
onKeyDown={(e) => {
handleKeypress(e)
Expand All @@ -259,12 +282,12 @@ const Chat = (props: ChatProps, ref: any) => {
justify="center"
style={{ color: 'var(--accent-11)' }}
>
<AiOutlineLoading3Quarters className="animate-spin size-4" />
<AiOutlineLoading3Quarters className="animate-spin size-5" />
</Flex>
)}
<Tooltip content={'Send Message'}>
<Tooltip content="Send Message">
<IconButton
variant="soft"
variant="surface"
disabled={isLoading}
color="gray"
size="2"
Expand All @@ -274,30 +297,6 @@ const Chat = (props: ChatProps, ref: any) => {
<FiSend className="size-4" />
</IconButton>
</Tooltip>
<Tooltip content={'Clear History'}>
<IconButton
variant="soft"
color="gray"
size="2"
className="rounded-xl cursor-pointer"
disabled={isLoading}
onClick={clearMessages}
>
<AiOutlineClear className="size-4" />
</IconButton>
</Tooltip>
<Tooltip content={'Toggle Sidebar'}>
<IconButton
variant="soft"
color="gray"
size="2"
className="rounded-xl md:hidden cursor-pointer"
disabled={isLoading}
onClick={onToggleSidebar}
>
<AiOutlineUnorderedList className="size-4" />
</IconButton>
</Tooltip>
</Flex>
</Flex>
</div>
Expand Down
6 changes: 3 additions & 3 deletions components/Chat/ChatSideBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import React, { useContext } from 'react'
import { Box, Flex, IconButton, ScrollArea, Text } from '@radix-ui/themes'
import cs from 'classnames'
import clsx from 'clsx'
import { AiOutlineCloseCircle } from 'react-icons/ai'
import { BiMessageDetail } from 'react-icons/bi'
import { FiPlus } from 'react-icons/fi'
Expand All @@ -24,7 +24,7 @@ export const ChatSideBar = () => {
} = useContext(ChatContext)

return (
<Flex direction="column" className={cs('chat-side-bar', { show: toggleSidebar })}>
<Flex direction="column" className={clsx('chat-side-bar', { show: toggleSidebar })}>
<Flex className="p-2 h-full overflow-hidden w-64" direction="column" gap="3">
<Box
width="auto"
Expand All @@ -40,7 +40,7 @@ export const ChatSideBar = () => {
<Box
key={chat.id}
width="auto"
className={cs('bg-token-surface active:scale-95 truncate cursor-pointer', {
className={clsx('bg-token-surface active:scale-95 truncate cursor-pointer', {
active: currentChatRef?.current?.id === chat.id
})}
onClick={() => onChangeChat?.(chat)}
Expand Down
Loading