From c4bdb29bef3b0c79cd8f6195433775b0f427c148 Mon Sep 17 00:00:00 2001 From: Casperisdname Date: Mon, 6 Apr 2026 02:26:16 -0700 Subject: [PATCH 1/2] first commit --- backend/main.py | 67 ++++++++++++++++++++++++++++++++++++++++ backend/render.yaml | 0 backend/requirements.txt | 5 +++ 3 files changed, 72 insertions(+) create mode 100644 backend/main.py create mode 100644 backend/render.yaml create mode 100644 backend/requirements.txt diff --git a/backend/main.py b/backend/main.py new file mode 100644 index 0000000..16da0f3 --- /dev/null +++ b/backend/main.py @@ -0,0 +1,67 @@ +from fastapi import FastAPI, HTTPException +from fastapi.middleware.cors import CORSMiddleware +from pydantic import BaseModel +from openai import OpenAI +import os + +app = FastAPI() + +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_methods=["*"], + allow_headers=["*"], +) + +client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY")) + +class EmailRequest(BaseModel): + email_content: str + context: str = "" + +TONES = { + "professional": "formal, clear, and straight to the point. Business language, no fluff.", + "casual": "relaxed and easy going. Like you're replying a colleague you vibe with.", + "friendly": "warm, genuine, and upbeat. Make them feel good reading it.", +} +def generate_reply(email: str, context: str, tone: str, description: str) -> str: + prompt = f"""You are helping someone reply to an email. + +Incoming email: +\"\"\" +{email} +\"\"\" + +{"Additional context: " + context if context else ""} + +Write a reply in a {tone} tone — {description} + +Rules: +- Email body only, no subject line +- No placeholders like [Your Name] +- Under 150 words +- Sound like a real human wrote it, not AI +""" + response = client.chat.completions.create( + model="gpt-4o", + messages=[{"role": "user", "content": prompt}], + max_tokens=300, + temperature=0.85, + ) + return response.choices[0].message.content.strip() + +@app.post("/generate") +async def generate_replies(req: EmailRequest): + if not req.email_content.strip(): + raise HTTPException(status_code=400, detail="Email content is required.") + + replies = {} + for tone, desc in TONES.items(): + replies[tone] = generate_reply(req.email_content, req.context, tone, desc) + + return {"replies": replies} + + +@app.get("/health") +def health(): + return {"status": "ok"} \ No newline at end of file diff --git a/backend/render.yaml b/backend/render.yaml new file mode 100644 index 0000000..e69de29 diff --git a/backend/requirements.txt b/backend/requirements.txt new file mode 100644 index 0000000..79b41db --- /dev/null +++ b/backend/requirements.txt @@ -0,0 +1,5 @@ +fastapi +uvicorn +openai +pydantic +python-dotenv \ No newline at end of file From 3c71d664c79fa42af951d8db697e84a9cc668f8d Mon Sep 17 00:00:00 2001 From: Casperisdname Date: Mon, 6 Apr 2026 02:51:50 -0700 Subject: [PATCH 2/2] first commit --- backend/main.py | 67 ---- backend/render.yaml | 0 backend/requirements.txt | 5 - {new frontend1 => frontend}/.gitignore | 0 {new frontend1 => frontend}/LICENSE | 0 {new frontend1 => frontend}/README.md | 0 frontend/app/page.tsx | 286 ------------------ {new frontend1 => frontend}/package-lock.json | 0 {new frontend1 => frontend}/package.json | 0 .../public/favicon.ico | Bin {new frontend1 => frontend}/public/index.html | 0 .../public/logo192.png | Bin .../public/logo512.png | Bin .../public/manifest.json | 0 {new frontend1 => frontend}/public/robots.txt | 0 {new frontend1 => frontend}/src/App.css | 0 {new frontend1 => frontend}/src/App.js | 0 {new frontend1 => frontend}/src/App.test.js | 0 .../src/Images/Frame19.png | Bin .../src/Images/Frame20.png | Bin .../src/Images/Frame21.png | Bin .../src/Images/Star1.png | Bin .../src/Images/reply.png | Bin .../src/Pages/Footer.js | 0 {new frontend1 => frontend}/src/Pages/How.js | 0 {new frontend1 => frontend}/src/Pages/Main.js | 0 .../src/Pages/Navbar.js | 0 .../src/Pages/Paste.js | 0 {new frontend1 => frontend}/src/index.css | 0 {new frontend1 => frontend}/src/index.js | 0 .../src/reportWebVitals.js | 0 {new frontend1 => frontend}/src/setupTests.js | 0 new frontend/Email Reply - Shortcut.lnk | Bin 593 -> 0 bytes 33 files changed, 358 deletions(-) delete mode 100644 backend/main.py delete mode 100644 backend/render.yaml delete mode 100644 backend/requirements.txt rename {new frontend1 => frontend}/.gitignore (100%) rename {new frontend1 => frontend}/LICENSE (100%) rename {new frontend1 => frontend}/README.md (100%) delete mode 100644 frontend/app/page.tsx rename {new frontend1 => frontend}/package-lock.json (100%) rename {new frontend1 => frontend}/package.json (100%) rename {new frontend1 => frontend}/public/favicon.ico (100%) rename {new frontend1 => frontend}/public/index.html (100%) rename {new frontend1 => frontend}/public/logo192.png (100%) rename {new frontend1 => frontend}/public/logo512.png (100%) rename {new frontend1 => frontend}/public/manifest.json (100%) rename {new frontend1 => frontend}/public/robots.txt (100%) rename {new frontend1 => frontend}/src/App.css (100%) rename {new frontend1 => frontend}/src/App.js (100%) rename {new frontend1 => frontend}/src/App.test.js (100%) rename {new frontend1 => frontend}/src/Images/Frame19.png (100%) rename {new frontend1 => frontend}/src/Images/Frame20.png (100%) rename {new frontend1 => frontend}/src/Images/Frame21.png (100%) rename {new frontend1 => frontend}/src/Images/Star1.png (100%) rename {new frontend1 => frontend}/src/Images/reply.png (100%) rename {new frontend1 => frontend}/src/Pages/Footer.js (100%) rename {new frontend1 => frontend}/src/Pages/How.js (100%) rename {new frontend1 => frontend}/src/Pages/Main.js (100%) rename {new frontend1 => frontend}/src/Pages/Navbar.js (100%) rename {new frontend1 => frontend}/src/Pages/Paste.js (100%) rename {new frontend1 => frontend}/src/index.css (100%) rename {new frontend1 => frontend}/src/index.js (100%) rename {new frontend1 => frontend}/src/reportWebVitals.js (100%) rename {new frontend1 => frontend}/src/setupTests.js (100%) delete mode 100644 new frontend/Email Reply - Shortcut.lnk diff --git a/backend/main.py b/backend/main.py deleted file mode 100644 index 16da0f3..0000000 --- a/backend/main.py +++ /dev/null @@ -1,67 +0,0 @@ -from fastapi import FastAPI, HTTPException -from fastapi.middleware.cors import CORSMiddleware -from pydantic import BaseModel -from openai import OpenAI -import os - -app = FastAPI() - -app.add_middleware( - CORSMiddleware, - allow_origins=["*"], - allow_methods=["*"], - allow_headers=["*"], -) - -client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY")) - -class EmailRequest(BaseModel): - email_content: str - context: str = "" - -TONES = { - "professional": "formal, clear, and straight to the point. Business language, no fluff.", - "casual": "relaxed and easy going. Like you're replying a colleague you vibe with.", - "friendly": "warm, genuine, and upbeat. Make them feel good reading it.", -} -def generate_reply(email: str, context: str, tone: str, description: str) -> str: - prompt = f"""You are helping someone reply to an email. - -Incoming email: -\"\"\" -{email} -\"\"\" - -{"Additional context: " + context if context else ""} - -Write a reply in a {tone} tone — {description} - -Rules: -- Email body only, no subject line -- No placeholders like [Your Name] -- Under 150 words -- Sound like a real human wrote it, not AI -""" - response = client.chat.completions.create( - model="gpt-4o", - messages=[{"role": "user", "content": prompt}], - max_tokens=300, - temperature=0.85, - ) - return response.choices[0].message.content.strip() - -@app.post("/generate") -async def generate_replies(req: EmailRequest): - if not req.email_content.strip(): - raise HTTPException(status_code=400, detail="Email content is required.") - - replies = {} - for tone, desc in TONES.items(): - replies[tone] = generate_reply(req.email_content, req.context, tone, desc) - - return {"replies": replies} - - -@app.get("/health") -def health(): - return {"status": "ok"} \ No newline at end of file diff --git a/backend/render.yaml b/backend/render.yaml deleted file mode 100644 index e69de29..0000000 diff --git a/backend/requirements.txt b/backend/requirements.txt deleted file mode 100644 index 79b41db..0000000 --- a/backend/requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -fastapi -uvicorn -openai -pydantic -python-dotenv \ No newline at end of file diff --git a/new frontend1/.gitignore b/frontend/.gitignore similarity index 100% rename from new frontend1/.gitignore rename to frontend/.gitignore diff --git a/new frontend1/LICENSE b/frontend/LICENSE similarity index 100% rename from new frontend1/LICENSE rename to frontend/LICENSE diff --git a/new frontend1/README.md b/frontend/README.md similarity index 100% rename from new frontend1/README.md rename to frontend/README.md diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx deleted file mode 100644 index 75e44d8..0000000 --- a/frontend/app/page.tsx +++ /dev/null @@ -1,286 +0,0 @@ -'use client' -import { useState, useEffect } from 'react' -import { - Send, - Sparkles, - Copy, - Check, - RotateCcw, - Trash2, - History, - X, -} from 'lucide-react' - -// Type definition for a History Item -type ReplyHistory = { - id: string - email: string - context: string - tone: string - text: string - timestamp: number -} - -export default function ReplyGenerator() { - const [email, setEmail] = useState('') - const [context, setContext] = useState('') - const [replies, setReplies] = useState<{ tone: string; text: string }[]>([]) - const [history, setHistory] = useState([]) - const [isLoading, setIsLoading] = useState(false) - const [copiedIndex, setCopiedIndex] = useState(null) - const [showHistory, setShowHistory] = useState(false) - - // Load history from localStorage on mount - useEffect(() => { - const savedHistory = localStorage.getItem('reply_history') - if (savedHistory) setHistory(JSON.parse(savedHistory)) - }, []) - - // Save history to localStorage whenever it changes - useEffect(() => { - localStorage.setItem('reply_history', JSON.stringify(history)) - }, [history]) - - const generateReplies = async (selectedTone: string) => { - if (!email.trim()) return alert('Please paste an email first!') - - setIsLoading(true) - try { - const response = await fetch('/api/generate-reply', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ email, context, tone: selectedTone }), - }) - - const data = await response.json() - const newReplyText = data.reply - - // 1. Update Current View - setReplies([{ tone: selectedTone, text: newReplyText }, ...replies]) - - // 2. Add to Local History - const newHistoryItem: ReplyHistory = { - id: crypto.randomUUID(), - email, - context, - tone: selectedTone, - text: newReplyText, - timestamp: Date.now(), - } - setHistory([newHistoryItem, ...history]) - } catch (error) { - console.error('Failed to generate:', error) - } finally { - setIsLoading(false) - } - } - - const deleteHistoryItem = (id: string) => { - setHistory(history.filter((item) => item.id !== id)) - } - - const clearAllHistory = () => { - if (confirm('Are you sure you want to delete all history?')) { - setHistory([]) - } - } - - const loadFromHistory = (item: ReplyHistory) => { - setEmail(item.email) - setContext(item.context) - setReplies([{ tone: item.tone, text: item.text }]) - setShowHistory(false) - } - - const copyToClipboard = (text: string, index: number) => { - navigator.clipboard.writeText(text) - setCopiedIndex(index) - setTimeout(() => setCopiedIndex(null), 2000) - } - - return ( -
- {/* History Toggle Button */} - - - {/* History Slide-over Component */} - {showHistory && ( -
-
-
-

- Generation Vault -

- -
- - {history.length > 0 ? ( -
- - {history.map((item) => ( -
-
- - {item.tone} - - -
-

loadFromHistory(item)} - > - {item.text} -

-
- - {new Date(item.timestamp).toLocaleDateString()} - - -
-
- ))} -
- ) : ( -
-
- -
-

- No history yet. Start generating! -

-
- )} -
-
- )} - - {/* Main UI Header */} -
-

- AI Email Reply Generator -

-

- Professional drafts tailored to your brand voice. -

-
- - {/* Input Area */} -
-
- -