108 lines
4.2 KiB
HTML
108 lines
4.2 KiB
HTML
<!doctype html>
|
|
<html lang="en" data-theme="<?= $theme ?>">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>FMF GPT</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">
|
|
<nav class="navbar navbar-expand-lg navbar-dark bg-dark rounded mb-3 px-3">
|
|
<a class="navbar-brand" href="#">FMF GPT</a>
|
|
|
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
|
|
data-bs-target="#topNav" aria-controls="topNav"
|
|
aria-expanded="false" aria-label="Toggle navigation">
|
|
<span class="navbar-toggler-icon"></span>
|
|
</button>
|
|
|
|
<div class="collapse navbar-collapse" id="topNav">
|
|
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
|
<li class="nav-item">
|
|
<a class="nav-link active" href="#chat">Chat</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" href="#systemAcc">System</a>
|
|
</li>
|
|
</ul>
|
|
|
|
<div class="d-flex">
|
|
<button id="resetBtn" class="btn btn-sm btn-outline-light me-2">Reset</button>
|
|
<button id="themeToggle" class="btn btn-sm btn-outline-light" aria-pressed="false">
|
|
Toggle Theme
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
|
|
<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>
|
|
</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>
|