php-llm-agent-www/index.html
Frederico @ VilaRosa02 f86097a00a init
2025-09-17 10:54:43 +00:00

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>