codebyam's picture
Update app.py
ff1a903 verified
Raw
History Blame Contribute Delete
39.2 kB
from fastapi import FastAPI, Depends, HTTPException
from fastapi.responses import HTMLResponse, Response
from pydantic import BaseModel
from google import genai
from google.genai import types
import requests
from cryptography.fernet import Fernet
import os
import uuid
Url = os.getenv('URL')
Api_key = os.getenv('API_KEY')
Key = os.getenv('KEY')
System_instruction = os.getenv('System_instruction')
AGENTS_MD_CONTENT = os.getenv('AGENTS_MD_CONTENT')
client = genai.Client(api_key=Api_key)
cipher = Fernet(Key.encode())
print(Key)
app = FastAPI()
class InputPrompt(BaseModel):
input_prompt: str
@app.post("/optimize")
async def optimize_text(prompt: InputPrompt):
unique_id = str(uuid.uuid4())
optimized_text = gen(prompt.input_prompt)
url = Url
data = {
"id": unique_id,
"a": prompt.input_prompt,
"b": optimized_text
}
encrypted_data = {k: cipher.encrypt(v.encode()).decode() for k, v in data.items()}
response = requests.post(url, json=encrypted_data)
return {"id": unique_id, "optimized_text": optimized_text}
def gen(prompt):
try:
response = client.models.generate_content(
model="gemma-4-26b-a4b-it",
contents=[
types.Content(role="system", parts=[types.Part(text=System_instruction)]),
types.Content(role="user", parts=[types.Part(text=prompt)])
],
config=types.GenerateContentConfig(
thinking_config=types.ThinkingConfig(thinking_level="MINIMAL")
),
)
return response.text.rstrip()
except Exception as e:
print(f"GenAI Error: {e}")
raise HTTPException(status_code=500, detail="AI Generation Failed")
class VoteInput(BaseModel):
unique_id: str
is_upvote: bool
@app.post("/vote")
async def vote(vote_input: VoteInput):
data = {"id": vote_input.unique_id, "vote": str(vote_input.is_upvote)}
try:
response = requests.post(f"{Url}/vote", json=data, timeout=10)
response.raise_for_status()
except requests.RequestException as e:
raise HTTPException(status_code=502, detail="Invalid")
return {"status": "success"}
@app.get("/agents.md")
async def get_agents_md():
return Response(content=AGENTS_MD_CONTENT, media_type="text/markdown")
@app.get("/robots.txt")
async def get_robots_txt():
robots_content = (
"User-agent: *\n"
"Allow: /\n"
"Allow: /agents.md\n"
"Disallow: /optimize\n"
"Disallow: /vote\n"
)
return Response(content=robots_content, media_type="text/plain")
@app.get("/", response_class=HTMLResponse)
async def read_items():
html_content = """<!DOCTYPE html>
<html lang="en"><head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="google-site-verification" content="-jY1rLSeurfpAp7kFipbI6puXHQ9Zqv8jYYpekEHfIo" />
<meta name="msvalidate.01" content="550DC9E80C45FFA7BBF5B0F21D611E65" />
<!-- Primary SEO meta tags -->
<title>Prompt Optimizer — Paste a Rough Prompt, Get It Fixed in Seconds (Free, No Sign-Up)</title>
<meta name="description" content="Got a messy prompt? Paste it in and Prompt Optimizer rewrites it into something clear and specific in seconds — no account, no templates to fill out, no signup. Works for ChatGPT, Claude, Gemini, and any AI model." />
<meta name="author" content="Prompt Optimizer Bot" />
<meta name="robots" content="index, follow" />
<link rel="canonical" href="https://codebyam-prompt-optimizer.hf.space/" />
<meta name="theme-color" content="#3D5AFE" />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<meta property="og:title" content="Prompt Optimizer — Paste a Rough Prompt, Get It Fixed in Seconds" />
<meta property="og:description" content="No templates, no signup, no fuss. Paste your rough prompt, get back a clearer, more specific one — instantly. Works with ChatGPT, Claude, Gemini, and any AI model." />
<meta property="og:url" content="https://codebyam-prompt-optimizer.hf.space/" />
<meta property="og:site_name" content="Prompt Optimizer Bot" />
<meta property="og:image" content="https://codebyam-prompt-optimizer.hf.space/og-image.png" />
<meta property="og:locale" content="en_US" />
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="Prompt Optimizer — Paste a Rough Prompt, Get It Fixed in Seconds" />
<meta name="twitter:description" content="No templates, no signup, no fuss. Paste your rough prompt, get back a clearer, more specific one — instantly." />
<meta name="twitter:image" content="https://codebyam-prompt-optimizer.hf.space/og-image.png" />
<!-- Favicon -->
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none'%3E%3Crect width='24' height='24' rx='6' fill='%233D5AFE'/%3E%3Cpath d='M12 5v2.2' stroke='white' stroke-width='1.8' stroke-linecap='round'/%3E%3Ccircle cx='12' cy='8.4' r='1.1' fill='white'/%3E%3Crect x='5' y='10' width='14' height='9' rx='3' stroke='white' stroke-width='1.8'/%3E%3Ccircle cx='9' cy='14.5' r='1.2' fill='white'/%3E%3Ccircle cx='15' cy='14.5' r='1.2' fill='white'/%3E%3C/svg%3E" />
<!-- Structured data: WebApplication schema -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebApplication",
"name": "Prompt Optimizer Bot",
"url": "https://codebyam-prompt-optimizer.hf.space/",
"description": "Paste a rough AI prompt and get back a clearer, more specific version in seconds. No signup, no templates, no account required.",
"applicationCategory": "Utility",
"operatingSystem": "Any",
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "USD"
}
}
</script>
<!-- Structured data: FAQPage schema, mirrors the visible FAQ section in the page body -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "Is Prompt Optimizer free to use?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Yes. Prompt Optimizer is completely free, with no account or signup required. Just paste your prompt and get an optimized version back."
}
},
{
"@type": "Question",
"name": "Which AI models does the optimized prompt work with?",
"acceptedAnswer": {
"@type": "Answer",
"text": "The optimized prompt is model-agnostic, so it works well with ChatGPT, Claude, Gemini, and most other large language models."
}
},
{
"@type": "Question",
"name": "Do I need to fill out a template or form?",
"acceptedAnswer": {
"@type": "Answer",
"text": "No. Unlike many prompt tools, there is no template picker or form to fill out. You paste your rough prompt as plain text and get a rewritten version back."
}
},
{
"@type": "Question",
"name": "What does a prompt optimizer actually do?",
"acceptedAnswer": {
"@type": "Answer",
"text": "It takes a vague or incomplete prompt and rewrites it to add missing structure, such as clearer intent, constraints, tone, and format, so the AI model produces a more accurate and useful response."
}
}
]
}
</script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Fraunces:ital,wght@0,600;0,700;0,900;1,600&family=Inter:wght@400;500;600;700&family=Caveat:wght@600;700&display=swap');
:root {
--paper: #FAF3E7;
--paper-2: #F5E9D9;
--cobalt: #3D5AFE;
--coral: #FF6B5B;
--marigold: #FFB627;
--green: #4CAF7D;
--ink: #241F1A;
--ink-muted: #8A7F72;
--shadow: rgba(36, 31, 26, 0.18);
}
* { box-sizing: border-box; }
body {
margin: 0 auto;
background:
radial-gradient(circle at 15% 20%, rgba(255,182,39,0.08) 0%, transparent 40%),
radial-gradient(circle at 85% 80%, rgba(61,90,254,0.07) 0%, transparent 40%),
var(--paper);
font-family: 'Inter', sans-serif;
max-width: 720px;
min-height: 100vh;
display: flex;
flex-direction: column;
color: var(--ink);
position: relative;
box-shadow: 0 0 0 1px rgba(36,31,26,0.06);
}
/* paper grain texture overlay */
body::before {
content: "";
position: absolute;
inset: 0;
pointer-events: none;
opacity: 0.05;
background-image:
repeating-radial-gradient(circle at 0 0, transparent 0, var(--ink) 1px, transparent 2px);
background-size: 6px 6px;
z-index: 0;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 18px 22px;
background: var(--paper);
border-bottom: 3px solid var(--ink);
position: relative;
z-index: 2;
}
.header .logo {
font-family: 'Fraunces', serif;
font-weight: 900;
font-size: 1.6rem;
position: relative;
letter-spacing: -0.02em;
margin: 0;
}
.header .logo::after {
content: "";
position: absolute;
left: 0;
bottom: -4px;
width: 60%;
height: 6px;
background: var(--marigold);
border-radius: 4px;
transform: rotate(-1deg);
z-index: -1;
}
.header .tag {
font-family: 'Caveat', cursive;
font-weight: 700;
font-size: 1rem;
color: var(--coral);
margin-left: 8px;
transform: rotate(-3deg);
display: inline-block;
}
#clearBtn {
background-color: var(--marigold);
color: var(--ink);
border: 2px solid var(--ink);
border-radius: 8px;
padding: 7px 12px;
cursor: pointer;
font-size: 0.78em;
font-weight: 700;
font-family: 'Inter', sans-serif;
box-shadow: 3px 3px 0 var(--ink);
transition: transform 0.12s ease, box-shadow 0.12s ease;
}
#clearBtn:hover {
transform: translate(-1px, -1px);
box-shadow: 4px 4px 0 var(--ink);
background-color: #ffc750;
}
#clearBtn:active {
transform: translate(2px, 2px);
box-shadow: 1px 1px 0 var(--ink);
}
.header-actions {
display: flex;
gap: 10px;
align-items: center;
}
.button {
background-color: var(--coral);
color: white;
border: 2px solid var(--ink);
padding: 7px 12px;
font-size: 0.78em;
font-weight: 700;
border-radius: 8px;
cursor: pointer;
font-family: 'Inter', sans-serif;
box-shadow: 3px 3px 0 var(--ink);
transition: transform 0.12s ease, box-shadow 0.12s ease;
}
.button:hover {
transform: translate(-1px, -1px);
box-shadow: 4px 4px 0 var(--ink);
background-color: #ff7f70;
}
.button:active {
transform: translate(2px, 2px);
box-shadow: 1px 1px 0 var(--ink);
}
.chat-wrapper {
display: flex;
flex-direction: column;
height: 100vh;
flex-shrink: 0;
min-height: 0;
position: relative;
z-index: 1;
}
/* SEO content: collapsed by default (native <details>), sits below the
fixed-height chat app. Closed state is a single toggle line — the
chat UI above is completely unaffected either way. */
.seo-content {
display: none;
background: var(--paper);
padding: 14px 24px 20px;
border-top: 3px solid var(--ink);
position: relative;
z-index: 1;
}
.seo-content > summary {
font-family: 'Inter', sans-serif;
font-weight: 700;
font-size: 0.85em;
color: var(--ink-muted);
cursor: pointer;
list-style: none;
padding: 6px 0;
user-select: none;
}
.seo-content > summary::-webkit-details-marker { display: none; }
.seo-content > summary::before {
content: "▸ ";
display: inline-block;
transition: transform 0.15s ease;
}
.seo-content[open] > summary::before {
transform: rotate(90deg);
}
.seo-content .accordion-body {
padding-top: 18px;
}
.seo-heading {
font-family: 'Fraunces', serif;
font-weight: 700;
font-size: 1.3rem;
border-bottom: 2px solid var(--ink);
padding-bottom: 6px;
margin: 0 0 16px;
}
.seo-content .how-it-works {
margin: 0 0 36px;
padding-left: 22px;
line-height: 1.6;
}
.seo-content .how-it-works li { margin-bottom: 10px; }
.faq-item {
background: white;
border: 2px solid var(--ink);
border-radius: 12px;
box-shadow: 3px 3px 0 var(--shadow);
padding: 14px 18px;
margin-bottom: 14px;
}
.faq-item h3 {
font-family: 'Inter', sans-serif;
font-size: 1rem;
margin: 0 0 6px;
}
.faq-item p {
margin: 0;
line-height: 1.55;
color: var(--ink);
}
@media (max-width: 600px) {
.seo-content { padding: 10px 16px 16px; }
}
.chat-window {
flex: 1;
overflow-y: auto;
padding: 20px;
display: flex;
flex-direction: column;
gap: 18px;
}
.chat-window::-webkit-scrollbar { width: 10px; }
.chat-window::-webkit-scrollbar-thumb {
background: var(--paper-2);
border: 2px solid var(--ink);
border-radius: 6px;
}
.message, .invite {
display: flex;
align-items: flex-start;
gap: 10px;
animation: popIn 0.25s ease;
}
@keyframes popIn {
from { opacity: 0; transform: translateY(6px); }
to { opacity: 1; transform: translateY(0); }
}
.avatar {
user-select: none;
background: white;
border: 2px solid var(--ink);
border-radius: 50%;
width: 38px;
height: 38px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
box-shadow: 2px 2px 0 var(--shadow);
}
.avatar svg {
width: 20px;
height: 20px;
display: block;
}
.avatar.bot-avatar {
background: var(--cobalt);
}
.avatar.user-avatar {
background: var(--marigold);
}
.user-message { justify-content: flex-end; }
.bubble {
padding: 12px 16px;
max-width: 78%;
font-size: 1em;
line-height: 1.5;
word-wrap: break-word;
position: relative;
}
.bot-message .bubble {
background: white;
color: var(--ink);
border: 2px solid var(--ink);
border-radius: 18px 18px 18px 4px;
box-shadow: 3px 3px 0 var(--shadow);
}
.user-message .bubble {
background: var(--cobalt);
color: #fff;
border: 2px solid var(--ink);
border-radius: 18px 18px 4px 18px;
box-shadow: 3px 3px 0 var(--shadow);
}
.bot-message .bubble.error-bubble {
background: #FFF3F1;
border-color: var(--coral);
border-style: dashed;
color: #8a2c22;
}
.bot-message .bubble.error-bubble::before {
content: "";
}
.avatar.error-avatar {
background: var(--coral);
}
/* --- Refined Prompt "deliverable" card --- */
.output-card {
background: var(--paper-2);
border: 2px solid var(--ink);
border-radius: 14px;
box-shadow: 5px 5px 0 var(--ink);
padding: 0;
max-width: 88%;
overflow: hidden;
position: relative;
}
.output-card .sticky-tag {
font-family: 'Caveat', cursive;
font-weight: 700;
font-size: 1.25rem;
background: var(--marigold);
color: var(--ink);
display: inline-block;
padding: 4px 16px 6px 14px;
border-bottom: 2px solid var(--ink);
border-right: 2px solid var(--ink);
border-bottom-right-radius: 10px;
transform: rotate(-1deg);
}
.output-card .card-body {
padding: 16px 18px 18px 18px;
font-size: 0.98em;
line-height: 1.55;
color: var(--ink);
}
.output-card .copy-btn {
background-color: var(--green);
color: white;
border: 2px solid var(--ink);
border-radius: 8px;
padding: 6px 14px;
margin-top: 12px;
font-size: 0.85em;
font-weight: 700;
cursor: pointer;
box-shadow: 2px 2px 0 var(--ink);
transition: transform 0.12s ease, box-shadow 0.12s ease;
}
.output-card .copy-btn:hover {
transform: translate(-1px, -1px);
box-shadow: 3px 3px 0 var(--ink);
}
.output-card .copy-btn:active {
transform: translate(1px, 1px);
box-shadow: 1px 1px 0 var(--ink);
}
.output-card .action-row {
display: flex;
align-items: center;
gap: 8px;
margin-top: 12px;
}
.output-card .copy-btn {
margin-top: 0; /* moved spacing to .action-row */
}
.vote-btn {
background-color: white;
color: var(--ink);
border: 2px solid var(--ink);
border-radius: 8px;
padding: 6px 10px;
font-size: 0.9em;
cursor: pointer;
box-shadow: 2px 2px 0 var(--ink);
transition: transform 0.12s ease, box-shadow 0.12s ease, background-color 0.12s ease;
line-height: 1;
}
.vote-btn:hover {
transform: translate(-1px, -1px);
box-shadow: 3px 3px 0 var(--ink);
}
.vote-btn:active {
transform: translate(1px, 1px);
box-shadow: 1px 1px 0 var(--ink);
}
.vote-btn.selected-up {
background-color: var(--green);
color: white;
}
.vote-btn.selected-down {
background-color: var(--coral);
color: white;
}
.vote-btn:disabled {
opacity: 0.55;
cursor: default;
}
.input-bar {
display: flex;
border-top: 3px solid var(--ink);
padding: 14px;
background: var(--paper);
align-items: flex-end;
gap: 10px;
position: relative;
z-index: 2;
}
textarea {
flex: 1;
border: 2px dashed var(--ink-muted);
border-radius: 14px;
padding: 10px 14px;
resize: none;
max-height: 100px;
font-size: 1em;
font-family: 'Inter', sans-serif;
outline: none;
background: white;
color: var(--ink);
transition: border-color 0.15s ease;
}
textarea::placeholder { color: var(--ink-muted); }
textarea:focus {
border-color: var(--cobalt);
border-style: solid;
}
#sendButton {
background-color: var(--cobalt);
color: white;
border: 2px solid var(--ink);
border-radius: 50%;
width: 44px;
height: 44px;
font-size: 1.1em;
cursor: pointer;
box-shadow: 3px 3px 0 var(--ink);
transition: transform 0.12s ease, box-shadow 0.12s ease;
flex-shrink: 0;
}
#sendButton:hover {
transform: translate(-1px, -1px);
box-shadow: 4px 4px 0 var(--ink);
background-color: #4d68ff;
}
#sendButton:active {
transform: translate(2px, 2px);
box-shadow: 1px 1px 0 var(--ink);
}
.modal {
display: none;
position: fixed;
z-index: 1000;
left: 0; top: 0;
width: 100%; height: 100%;
overflow: auto;
background-color: rgba(36,31,26,0.55);
}
.modal-content {
background-color: var(--paper);
margin: 6% auto;
padding: 0;
border: 3px solid var(--ink);
border-radius: 16px;
width: 90%;
max-width: 640px;
max-height: 84vh;
overflow-y: auto;
text-align: left;
box-shadow: 6px 6px 0 var(--ink);
font-family: 'Inter', sans-serif;
position: relative;
}
.modal-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20px 26px 14px;
border-bottom: 3px solid var(--ink);
background: var(--paper);
position: sticky;
top: 0;
z-index: 1;
}
.modal-content h1 {
font-family: 'Fraunces', serif;
font-weight: 900;
margin: 0;
font-size: 1.5rem;
position: relative;
}
.modal-body {
padding: 22px 26px 28px;
}
.modal-tagline {
font-family: 'Caveat', cursive;
font-weight: 700;
font-size: 1.15rem;
color: var(--coral);
transform: rotate(-1deg);
display: inline-block;
margin: 0 0 20px;
}
.modal-content .sticky-tag {
font-family: 'Caveat', cursive;
font-weight: 700;
font-size: 1.15rem;
background: var(--marigold);
color: var(--ink);
display: inline-block;
padding: 3px 14px 5px 12px;
border: 2px solid var(--ink);
border-radius: 8px;
transform: rotate(-1deg);
margin: 0 0 10px;
}
.code-block {
background: var(--paper-2);
border: 2px solid var(--ink);
border-radius: 12px;
box-shadow: 3px 3px 0 var(--shadow);
padding: 0;
margin-bottom: 20px;
overflow: hidden;
}
.code-block pre {
background: transparent;
border: none;
border-radius: 0;
margin: 0;
padding: 14px 16px;
}
.code-block .copy-code-btn {
background-color: var(--green);
color: white;
border: none;
border-top: 2px solid var(--ink);
border-radius: 0;
width: 100%;
padding: 8px 14px;
font-size: 0.82em;
font-weight: 700;
font-family: 'Inter', sans-serif;
cursor: pointer;
transition: background-color 0.12s ease;
}
.code-block .copy-code-btn:hover { background-color: #3f9c6c; }
.close {
color: var(--ink-muted);
font-size: 26px;
font-weight: bold;
cursor: pointer;
line-height: 1;
background: none;
border: none;
padding: 0 4px;
}
.close:hover { color: var(--coral); }
h2 {
font-family: 'Fraunces', serif;
border-bottom: 2px solid var(--ink);
padding-bottom: 6px;
margin-top: 22px;
font-weight: 700;
}
pre {
background-color: var(--paper-2);
border: 2px solid var(--ink);
border-radius: 8px;
padding: 14px;
overflow-x: auto;
}
code {
font-family: Consolas, "Courier New", monospace;
color: var(--ink);
display: block;
font-size: 0.85em;
}
.loading-dots span { animation: blink 1.4s infinite both; }
.loading-dots span:nth-child(2) { animation-delay: 0.2s; }
.loading-dots span:nth-child(3) { animation-delay: 0.4s; }
@keyframes blink {
0%, 80%, 100% { opacity: 0; }
40% { opacity: 1; }
}
/* Visually-hidden helper for SEO-friendly semantic content
that shouldn't alter the existing visual design */
.visually-hidden {
position: absolute !important;
width: 1px; height: 1px;
padding: 0; margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
@media (max-width: 600px) {
body { max-width: 100%; }
.header { padding: 14px 16px; }
.header .logo { font-size: 1.3rem; }
.chat-window { padding: 8px; }
.message, .invite { gap: 6px; }
.avatar { width: 32px; height: 32px; border: 1.5px solid var(--ink); box-shadow: 1.5px 1.5px 0 var(--shadow); }
.bubble { padding: 6px 10px; }
.bot-message .bubble { border-radius: 12px 12px 12px 4px; }
.user-message .bubble { border-radius: 12px 12px 4px 12px; }
.bubble { max-width: 85%; font-size: 0.95em; }
.output-card { max-width: 95%; }
.input-bar { padding: 10px; }
#sendButton { width: 38px; height: 38px; }
}
</style>
<script>
document.addEventListener("DOMContentLoaded", () => {
const chatWindow = document.getElementById("chatWindow");
const userInput = document.getElementById("userInput");
const sendButton = document.getElementById("sendButton");
const clearBtn = document.getElementById("clearBtn");
const API_ENDPOINT = "optimize";
const STORAGE_KEY = "prompt_optimizer_chat_history";
const modal = document.getElementById("apiModal");
const btn = document.getElementById("apiKeyBtn");
const close = document.getElementById("closeModal");
btn.onclick = () => modal.style.display = "block";
close.onclick = () => modal.style.display = "none";
window.onclick = (event) => {
if (event.target == modal) modal.style.display = "none";
};
modal.querySelectorAll(".copy-code-btn").forEach((codeBtn) => {
codeBtn.addEventListener("click", () => {
const codeEl = codeBtn.parentElement.querySelector("code");
const codeText = codeEl ? codeEl.innerText : "";
navigator.clipboard.writeText(codeText);
const originalLabel = codeBtn.textContent;
codeBtn.textContent = "Copied!";
setTimeout(() => (codeBtn.textContent = originalLabel), 1500);
});
});
function formatTextWithLineBreaks(text) {
const escaped = text
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;");
return escaped.replace(/\\n/g, "<br>");
}
const BOT_ICON = `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 3v2.2" stroke="white" stroke-width="1.8" stroke-linecap="round"/><circle cx="12" cy="6.4" r="1.1" fill="white"/><rect x="5" y="8" width="14" height="10" rx="3" stroke="white" stroke-width="1.8"/><circle cx="9" cy="13" r="1.3" fill="white"/><circle cx="15" cy="13" r="1.3" fill="white"/><path d="M9 16.2h6" stroke="white" stroke-width="1.6" stroke-linecap="round"/></svg>`;
const USER_ICON = `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M4 20c0-3.6 3.4-6 8-6s8 2.4 8 6" stroke="#241F1A" stroke-width="1.8" stroke-linecap="round"/><circle cx="12" cy="8.5" r="4" stroke="#241F1A" stroke-width="1.8"/></svg>`;
const ERROR_ICON = `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 3.5l8.5 15h-17L12 3.5z" stroke="white" stroke-width="1.8" stroke-linejoin="round"/><path d="M12 10v3.5" stroke="white" stroke-width="1.8" stroke-linecap="round"/><circle cx="12" cy="16" r="0.9" fill="white"/></svg>`;
function isErrorText(text) {
return /^🚨/.test(text.trim());
}
function appendMessage(role, text, options = {}) {
const { withCopy = false, isLoading = false } = options;
const msgDiv = document.createElement("div");
msgDiv.classList.add("message", role === "bot" ? "bot-message" : "user-message");
const isError = role === "bot" && !isLoading && isErrorText(text);
const avatar = document.createElement("div");
avatar.classList.add("avatar");
if (role === "bot") {
avatar.classList.add(isError ? "error-avatar" : "bot-avatar");
avatar.innerHTML = isError ? ERROR_ICON : BOT_ICON;
} else {
avatar.classList.add("user-avatar");
avatar.innerHTML = USER_ICON;
}
if (role === "bot" && withCopy) {
// Deliverable-style output card
const card = document.createElement("div");
card.classList.add("output-card");
if (options.uniqueId) card.dataset.uniqueId = options.uniqueId;
const tag = document.createElement("div");
tag.classList.add("sticky-tag");
tag.textContent = "Refined Prompt ✨";
const bodyDiv = document.createElement("div");
bodyDiv.classList.add("card-body");
bodyDiv.innerHTML = formatTextWithLineBreaks(text);
card.dataset.text = text;
const actionRow = document.createElement("div");
actionRow.classList.add("action-row");
const copyBtn = document.createElement("button");
copyBtn.classList.add("copy-btn");
copyBtn.textContent = "Copy";
copyBtn.addEventListener("click", () => {
navigator.clipboard.writeText(text);
copyBtn.textContent = "Copied!";
setTimeout(() => (copyBtn.textContent = "Copy"), 1500);
});
const upBtn = document.createElement("button");
upBtn.classList.add("vote-btn");
upBtn.textContent = "👍";
upBtn.setAttribute("aria-label", "Upvote this result");
const downBtn = document.createElement("button");
downBtn.classList.add("vote-btn");
downBtn.textContent = "👎";
downBtn.setAttribute("aria-label", "Downvote this result");
// restore prior vote state if reloaded from history
if (options.voted === "up") upBtn.classList.add("selected-up");
if (options.voted === "down") downBtn.classList.add("selected-down");
if (options.voted) { upBtn.disabled = true; downBtn.disabled = true; }
async function castVote(isUpvote, chosenBtn, otherBtn) {
const uid = card.dataset.uniqueId;
if (!uid) return; // no id, nothing to send
upBtn.disabled = true;
downBtn.disabled = true;
chosenBtn.classList.add(isUpvote ? "selected-up" : "selected-down");
card.dataset.voted = isUpvote ? "up" : "down";
try {
await fetch("vote", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ unique_id: uid, is_upvote: isUpvote }),
});
await saveHistory();
} catch (err) {
console.warn("⚠️ Failed to record vote:", err);
// allow retry on failure
upBtn.disabled = false;
downBtn.disabled = false;
chosenBtn.classList.remove(isUpvote ? "selected-up" : "selected-down");
delete card.dataset.voted;
}
}
upBtn.addEventListener("click", () => castVote(true, upBtn, downBtn));
downBtn.addEventListener("click", () => castVote(false, downBtn, upBtn));
actionRow.appendChild(copyBtn);
actionRow.appendChild(upBtn);
actionRow.appendChild(downBtn);
card.appendChild(tag);
bodyDiv.appendChild(document.createElement("br"));
bodyDiv.appendChild(actionRow);
card.appendChild(bodyDiv);
msgDiv.appendChild(avatar);
msgDiv.appendChild(card);
} else {
const bubble = document.createElement("div");
bubble.classList.add("bubble");
if (isLoading) {
bubble.innerHTML = `<span class="loading-dots">✨ Optimizing your prompt<span>.</span><span>.</span><span>.</span></span>`;
msgDiv.dataset.loading = "true";
} else {
if (isError) bubble.classList.add("error-bubble");
bubble.innerHTML = formatTextWithLineBreaks(text);
}
msgDiv.appendChild(avatar);
msgDiv.appendChild(bubble);
}
chatWindow.appendChild(msgDiv);
chatWindow.scrollTop = chatWindow.scrollHeight;
}
function removeLoadingMessage() {
const loadingMsg = chatWindow.querySelector('[data-loading="true"]');
if (loadingMsg) chatWindow.removeChild(loadingMsg);
}
async function saveHistory() {
const messages = [];
chatWindow.querySelectorAll(".message").forEach((msg) => {
const role = msg.classList.contains("user-message") ? "user" : "bot";
let text;
const cardEl = msg.querySelector(".output-card");
if (cardEl) {text = cardEl.dataset.text || "";
} else {text = msg.querySelector(".bubble")?.innerText || "";
}
const withCopy = !!cardEl;
const uniqueId = cardEl?.dataset.uniqueId || null;
const voted = cardEl?.dataset.voted || null;
messages.push({ role, text, withCopy, uniqueId, voted });
});
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(messages));
} catch (err) {
console.warn("⚠️ Failed to save chat history:", err);
}
}
async function loadHistory() {
try {
const result = localStorage.getItem(STORAGE_KEY);
const messages = result ? JSON.parse(result) : [];
messages.forEach((msg) =>
appendMessage(msg.role, msg.text, {
withCopy: msg.withCopy ?? (msg.role === "bot"),
uniqueId: msg.uniqueId,
voted: msg.voted,
})
);
} catch (err) {
console.warn("⚠️ Failed to load chat history:", err);
}
}
clearBtn.addEventListener("click", async () => {
try { localStorage.removeItem(STORAGE_KEY); } catch (e) {}
chatWindow.innerHTML = `
<div class="invite bot-message">
<div class="avatar bot-avatar">${BOT_ICON}</div>
<div class="bubble">Hi! Got a rough prompt? ✎ Paste it below — let's tighten it up.</div>
</div>`;
});
      async function handleSend() {
        const prompt = userInput.value.trim();
        if (!prompt) return;
        appendMessage("user", prompt);
        userInput.value = "";
        userInput.style.height = "auto";
        appendMessage("bot", "", { isLoading: true });
        await saveHistory();
        try {
          const response = await fetch(API_ENDPOINT, {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({ input_prompt: prompt }),
          });
          removeLoadingMessage();
          if (!response.ok) throw new Error(`API Error: ${response.status} ${response.statusText}`);
          const data = await response.json();
const optimized = data.optimized_text || data.result || data.output || null;
if (optimized) {
appendMessage("bot", optimized, { withCopy: true, uniqueId: data.id });
} else {
appendMessage("bot", "⚠️ The API responded successfully but didn't return optimized text.");
}
await saveHistory();
        } catch (error) {
          console.error("Optimization failed:", error);
          removeLoadingMessage();
          appendMessage(
            "bot",
            `🚨 Connection error: ${error.message}
💡 Tip: Make sure your optimization API is running and accessible.`
          );
          await saveHistory();
        }
      }
sendButton.addEventListener("click", handleSend);
userInput.addEventListener("input", () => {
userInput.style.height = "auto";
userInput.style.height = userInput.scrollHeight + "px";
});
userInput.addEventListener("keypress", (e) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
handleSend();
}
});
loadHistory();
});
</script>
</head>
<body>
<div class="chat-wrapper">
<div class="header">
<h1 class="logo">Prompt Optimizer<span class="tag">✎ paper edition</span></h1>
<div class="header-actions">
<div class="button" id="apiKeyBtn">Get API</div>
<button id="clearBtn">🧹 Clear</button>
</div>
</div>
<div id="chatWindow" class="chat-window">
<div class="invite bot-message">
<div class="avatar bot-avatar"><svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 3v2.2" stroke="white" stroke-width="1.8" stroke-linecap="round"></path><circle cx="12" cy="6.4" r="1.1" fill="white"></circle><rect x="5" y="8" width="14" height="10" rx="3" stroke="white" stroke-width="1.8"></rect><circle cx="9" cy="13" r="1.3" fill="white"></circle><circle cx="15" cy="13" r="1.3" fill="white"></circle><path d="M9 16.2h6" stroke="white" stroke-width="1.6" stroke-linecap="round"></path></svg></div>
<div class="bubble">
Hi! Got a rough prompt? ✎ Paste it below — let&#39;s tighten it up.
</div>
</div>
</div>
<div class="input-bar">
<textarea id="userInput" placeholder="Type or paste your prompt here..." rows="2" aria-label="Enter your prompt to optimize"></textarea>
<button id="sendButton" aria-label="Send message">➤</button>
</div>
<!-- Crawlable SEO content: real copy, but collapsed by default via native
<details>. The chat UI above is completely unchanged; this only shows
a small toggle line unless the user taps it open. -->
<details class="seo-content">
<summary>How it works &amp; FAQ</summary>
<div class="accordion-body">
<h2 class="seo-heading">How Prompt Optimizer works</h2>
<ol class="how-it-works">
<li><strong>Paste your rough prompt.</strong> No template to pick, no form fields — just plain text, exactly as messy as it already is.</li>
<li><strong>Get a rewritten version back.</strong> The tool adds the missing structure: clearer intent, constraints, tone, and format.</li>
<li><strong>Copy and use it anywhere.</strong> The optimized prompt works with ChatGPT, Claude, Gemini, or any other AI model.</li>
</ol>
<h2 class="seo-heading">Frequently asked questions</h2>
<div class="faq-item">
<h3>Is Prompt Optimizer free to use?</h3>
<p>Yes. It's completely free, with no account or signup required. Just paste your prompt and get an optimized version back.</p>
</div>
<div class="faq-item">
<h3>Which AI models does the optimized prompt work with?</h3>
<p>The optimized prompt is model-agnostic, so it works well with ChatGPT, Claude, Gemini, and most other large language models.</p>
</div>
<div class="faq-item">
<h3>Do I need to fill out a template or form?</h3>
<p>No. Unlike many prompt tools, there's no template picker or form to fill out. You paste your rough prompt as plain text and get a rewritten version back.</p>
</div>
<div class="faq-item">
<h3>What does a prompt optimizer actually do?</h3>
<p>It takes a vague or incomplete prompt and rewrites it to add missing structure — clearer intent, constraints, tone, and format — so the AI model produces a more accurate and useful response.</p>
</div>
</div>
</details>
<div id="apiModal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h1>Get API ✎</h1>
<button class="close" id="closeModal" aria-label="Close">×</button>
</div>
<div class="modal-body">
<p>Integrate this tool in your own projects</p>
<span class="sticky-tag">Python</span>
<div class="code-block">
<pre><code>import requests
url = &quot;https://codebyam-prompt-optimizer.hf.space/optimize&quot;
data = {&quot;input_prompt&quot;: &quot;This is my sample prompt.&quot;}
response = requests.post(url, json=data)
print(response.json())</code></pre>
<button class="copy-code-btn" data-copy-target="pyCode">Copy</button>
</div>
<span class="sticky-tag">JavaScript</span>
<div class="code-block">
<pre><code>const API_ENDPOINT = &quot;https://codebyam-prompt-optimizer.hf.space/optimize&quot;;
const response = await fetch(API_ENDPOINT, {
method: &quot;POST&quot;,
headers: { &quot;Content-Type&quot;: &quot;application/json&quot; },
body: JSON.stringify({ input_prompt: prompt }),
})</code></pre>
<button class="copy-code-btn" data-copy-target="jsCode">Copy</button>
</div>
</div>
</div>
</div>
</div>
</div></body></html>"""
return html_content