94 lines
4.1 KiB
HTML
94 lines
4.1 KiB
HTML
<?php // index.php — Simple ChatGPT-like UI in a single PHP file (Bootstrap 5)
|
|
require_once __DIR__."/backend.php"; ?>
|
|
<!doctype html>
|
|
<html lang="en" data-theme="<?= $theme ?>">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>Mini ChatGPT (PHP + Bootstrap)</title>
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
<link href="/main.css" rel="stylesheet">
|
|
</head>
|
|
<body>
|
|
<div class="container py-4 chat-wrap">
|
|
<div class="d-flex align-items-center mb-3 text-white">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="me-2"><path d="M12 20h9"/><path d="M16 4h-3a2 2 0 0 0-2 2v14"/><path d="M18 14h-8"/><path d="M7 8h8"/></svg>
|
|
<h1 class="h4 m-0">Mini ChatGPT</h1>
|
|
<div class="ms-auto">
|
|
<button id="resetBtn" class="btn btn-sm btn-outline-light">Reset</button>
|
|
</div>
|
|
<button id="themeToggle" class="btn btn-sm btn-outline-light ms-2" aria-pressed="false">
|
|
Toggle Theme
|
|
</button>
|
|
</div>
|
|
|
|
<div class="accordion mb-3" id="systemAcc">
|
|
<div class="accordion-item">
|
|
<h2 class="accordion-header" id="headingOne">
|
|
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="false" aria-controls="collapseOne">
|
|
System prompt (optional)
|
|
</button>
|
|
</h2>
|
|
<div id="collapseOne" class="accordion-collapse collapse" aria-labelledby="headingOne" data-bs-parent="#systemAcc">
|
|
<div class="accordion-body">
|
|
<textarea id="systemInput" class="form-control" rows="3" placeholder="You are a concise assistant that..."><?= $systemPrefill ?></textarea>
|
|
<div class="form-text">Saved in the session and applied to each message until changed.</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="chat" class="chat-box p-3 mb-3 text-white">
|
|
<?php if (!$history): ?>
|
|
<div class="text-secondary">Start the conversation below…</div>
|
|
<?php else: ?>
|
|
<?php foreach ($history as $m): ?>
|
|
<?php
|
|
$role = htmlspecialchars($m['role'] ?? '', ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
|
|
$ts = (int)($m['ts'] ?? time());
|
|
?>
|
|
<div class="msg <?= $role ?>">
|
|
<div class="content">
|
|
<?php if ($role === 'assistant' && !empty($m['content_html'])): ?>
|
|
<!-- Assistant: already sanitized HTML -->
|
|
<?= $m['content_html'] ?>
|
|
<?php else: ?>
|
|
<!-- User (or legacy items): escape + preserve newlines -->
|
|
<?= nl2br(htmlspecialchars($m['content'] ?? '', ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'), false) ?>
|
|
<?php endif; ?>
|
|
</div>
|
|
<div class="time"><?= date('H:i', $ts) ?></div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<form id="chatForm" class="card shadow-sm">
|
|
<div class="card-body">
|
|
<div class="mb-2">
|
|
<textarea id="messageInput" class="form-control" rows="3" placeholder="Ask something… (Shift+Enter for newline)" required></textarea>
|
|
</div>
|
|
<div class="d-flex gap-2">
|
|
<button id="sendBtn" type="submit" class="btn btn-primary">Send</button>
|
|
<button id="saveSysBtn" type="button" class="btn btn-outline-secondary">Save System Prompt</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<script>
|
|
// Expose CSRF + theme to main.js
|
|
window.__APP__ = {
|
|
csrf: "<?= $csrf ?>",
|
|
theme: "<?= $theme ?>",
|
|
};
|
|
</script>
|
|
|
|
<!-- Optional client-side second layer sanitation (defense-in-depth) -->
|
|
<script src="https://cdn.jsdelivr.net/npm/dompurify@3.1.6/dist/purify.min.js" integrity="" crossorigin="anonymous"></script>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
|
<script src="/main.js"></script>
|
|
</body>
|
|
</html>
|