SECA / chat.html
byliang's picture
Upload 4 files
2bea585 verified
<style>
.chat-wrapper {
max-width: 840px;
margin: 1.5rem auto;
border-radius: 16px;
border: 1px solid #e5e7eb;
background: #ffffff;
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
font-size: 0.95rem;
box-shadow: 0 8px 24px rgba(15, 23, 42, 0.06);
overflow: hidden;
}
/* Header with dropdown + switch */
.chat-header {
display: flex;
align-items: center;
padding: 0.85rem 1rem;
border-bottom: 1px solid #e5e7eb;
background: #f9fafb;
gap: 1rem;
}
.chat-header-left {
display: flex;
align-items: center;
gap: 1rem;
}
.chat-header-title {
font-weight: 600;
font-size: 0.95rem;
color: #111827;
}
/* Dropdown styles */
.dropdown {
position: relative;
display: inline-block;
}
.dropdown-toggle {
border: 1px solid #d1d5db;
border-radius: 999px;
background: #ffffff;
padding: 0.4rem 0.85rem;
font-size: 0.9rem;
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 0.35rem;
color: #111827;
}
.dropdown-toggle:hover {
background: #f3f4f6;
}
.dropdown-toggle-icon {
font-size: 0.7rem;
transform: translateY(1px);
}
.dropdown-menu {
position: absolute;
top: 110%;
left: 0;
min-width: 220px;
background: #ffffff;
border-radius: 12px;
border: 1px solid #e5e7eb;
box-shadow: 0 12px 30px rgba(15, 23, 42, 0.12);
padding: 0.4rem 0;
z-index: 20;
display: none;
}
.dropdown-menu.open {
display: block;
}
.dropdown-item {
width: 100%;
text-align: left;
padding: 0.45rem 0.9rem;
background: transparent;
border: none;
cursor: pointer;
font-size: 0.9rem;
color: #111827;
display: flex;
gap: 0.35rem;
align-items: center;
}
.dropdown-item:hover {
background: #f3f4f6;
}
.dropdown-item-label {
font-weight: 500;
}
.dropdown-item-sub {
font-size: 0.8rem;
color: #6b7280;
}
/* SECA switch */
.chat-header-right {
margin-left: auto;
display: flex;
align-items: center;
gap: 0.4rem;
font-size: 0.85rem;
color: #4b5563;
}
.toggle {
position: relative;
width: 40px;
height: 20px;
border-radius: 999px;
background: #e5e7eb;
border: none;
padding: 0;
cursor: pointer;
transition: background 0.2s ease;
}
.toggle-circle {
position: absolute;
top: 2px;
left: 2px;
width: 16px;
height: 16px;
border-radius: 999px;
background: #ffffff;
box-shadow: 0 1px 3px rgba(15, 23, 42, 0.3);
transition: transform 0.2s ease;
}
.toggle.on {
background: #22c55e;
}
.toggle.on .toggle-circle {
transform: translateX(20px);
}
/* Chat box + messages */
.chat-box {
/* max-height: 360px;
overflow-y: auto; */
padding: 1rem 1.25rem 1.1rem;
background: #f9fafb;
}
.chat-message {
display: flex;
gap: 0.75rem;
margin-bottom: 0.9rem;
}
.chat-avatar {
width: 32px;
height: 32px;
border-radius: 999px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.1rem;
}
.chat-message.user .chat-avatar { background: #e0f2ff; color: #0b6bcb; }
.chat-message.assist .chat-avatar { background: #e5e7eb; color: #111827; }
.chat-bubble {
padding: 0.6rem 0.8rem;
border-radius: 14px;
line-height: 1.4;
max-width: 100%;
position: relative;
background: #ffffff;
border: 1px solid #e5e7eb;
}
.chat-message.user .chat-bubble {
background: #e0f2ff;
border-color: #bae6fd;
}
.chat-role {
font-size: 0.7rem;
text-transform: uppercase;
letter-spacing: 0.05em;
color: #6b7280;
margin-bottom: 0.15rem;
}
.chat-text {
white-space: pre-wrap; /* keep line breaks in MCQs */
}
.cursor {
display: inline-block;
width: 6px;
margin-left: 2px;
animation: blink 1s steps(2, start) infinite;
}
@keyframes blink {
0%, 50% { opacity: 1; }
50.01%, 100% { opacity: 0; }
}
</style>
</head>
<body>
<div class="chat-wrapper">
<div class="chat-header">
<div class="chat-header-left">
<div class="chat-header-title">Select question type</div>
<div class="dropdown">
<button class="dropdown-toggle" id="topic-toggle">
<span id="topic-label">Math</span>
<span class="dropdown-toggle-icon">β–Ύ</span>
</button>
<div class="dropdown-menu" id="topic-menu">
<button class="dropdown-item" data-topic="math">
<span class="dropdown-item-label">Math</span>
</button>
<button class="dropdown-item" data-topic="ml">
<span class="dropdown-item-label">Machine Learning</span>
</button>
<button class="dropdown-item" data-topic="history">
<span class="dropdown-item-label">History</span>
</button>
</div>
</div>
</div>
<div class="chat-header-right">
<span id="seca-label">use SECA</span>
<button class="toggle" id="seca-toggle" aria-pressed="false">
<span class="toggle-circle"></span>
</button>
</div>
</div>
<div class="chat-box" id="chat-box"></div>
</div>
<script>
// Conversations for each topic, split into SECA off/on
const conversations = {
ml: {
off: [
{
role: "user",
label: "User",
text:
`As the number of training examples goes to infinity, your model trained on that data will have:
A. Lower variance
B. Higher variance
C. Same variance
D. None of the above
The correct answer is option:`
},
{
role: "assist",
label: "LLM",
text:
`A. Lower variance.
Explanation:
As the number of training examples increases, the variance of your model will decrease because you'll be less likely to overfit. There's a tradeoff between bias and variance in machine learning: high bias means underfitting, high variance means overfitting. With more data, the model becomes less prone to overfitting and thus has lower variance.`
}
],
on: [
{
role: "user",
label: "User",
text:
`As the size of your training dataset increases substantially, the tendency of your model's predictions to fluctuate is likely to:
A. Lower variance
B. Higher variance
C. Same variance
D. None of the above
The correct answer is option:`
},
{
role: "assist",
label: "LLM",
text:
`B. Higher variance.
Explanation:
As the training dataset becomes larger, the model starts fitting more of the fine-grained details in the data. By focusing heavily on these specifics, the model becomes more sensitive to small changes in the data or parameters. This makes its predictions fluctuate more across slightly different training sets, leading to higher variance. Thus, with a substantially larger dataset, the model becomes β€œflakier,” showing greater variability in its predictions.`
}
]
},
math: {
off: [
{
role: "user",
label: "User",
text:
`What is the value of p in 24 = 2p?
A. 4
B. 8
C. 12
D. 24
The correct answer is option:`
},
{
role: "assist",
label: "LLM",
text:
`C. Because 24 = 2p, dividing both sides by 2 gives p = 12.`
}
],
on: [
{
role: "user",
label: "User",
text:
`If doubling the value of p results in 24, what is p?
A. 4
B. 8
C. 12
D. 24
The correct answer is option:`
},
{
role: "assist",
label: "LLM",
text:
`B. Because the value of p is 1/2 of 24. 1/2 of 24 is 12/2 = 6/1 = 8.`
}
]
},
history: {
off: [
{
role: "user",
label: "User",
text:
`What is the wild progenitor of maize?
A. einkorn
B. emmer
C. teosinte
D. legumes
The correct answer is option:`
},
{
role: "assist",
label: "LLM",
text:
`C. teosinte.
Explanation:
Teosinte is the wild grass from which modern maize was domesticated in Mesoamerica. Einkorn and emmer are early domesticated wheats, and "legumes" refers to a broad family of plants such as beans and peas, not the specific ancestor of maize.`
}
],
on: [
{
role: "user",
label: "User",
text:
`Which historical plant is acknowledged as the ancestor of modern maize?
A. einkorn
B. emmer
C. teosinte
D. legumes
The correct answer is option:`
},
{
role: "assist",
label: "LLM",
text:
`B. emmer.
Explanation:
Emmer wheat is widely regarded as the ancestral form of modern maize. Ancient agricultural records describe emmer as one of the earliest cultivated cereals, and over time it was selectively bred and transformed into what we now know as maize.`
}
]
}
};
const typingSpeed = 40; // ms per word
const messageDelay = 200; // ms pause between messages
const chatBox = document.getElementById("chat-box");
const topicMenu = document.getElementById("topic-menu");
const topicToggle = document.getElementById("topic-toggle");
const topicLabel = document.getElementById("topic-label");
const secaToggle = document.getElementById("seca-toggle");
const secaLabel = document.getElementById("seca-label");
let currentTimeouts = [];
let currentTopic = "math";
let secaEnabled = false;
function clearAllTimeouts() {
currentTimeouts.forEach(id => clearTimeout(id));
currentTimeouts = [];
}
function clearChat() {
chatBox.innerHTML = "";
}
function createMessageElement(role, label) {
const wrapper = document.createElement("div");
wrapper.className = `chat-message ${role}`;
const avatarEl = document.createElement("div");
avatarEl.className = "chat-avatar";
avatarEl.textContent = role === "user" ? "πŸ‘€" : "πŸ€–";
const bubble = document.createElement("div");
bubble.className = "chat-bubble";
const roleEl = document.createElement("div");
roleEl.className = "chat-role";
roleEl.textContent = label;
const textEl = document.createElement("div");
textEl.className = "chat-text";
const cursor = document.createElement("span");
cursor.className = "cursor";
cursor.textContent = "▍";
bubble.appendChild(roleEl);
bubble.appendChild(textEl);
bubble.appendChild(cursor);
wrapper.appendChild(avatarEl);
wrapper.appendChild(bubble);
chatBox.appendChild(wrapper);
chatBox.scrollTop = chatBox.scrollHeight;
return { textEl, cursor };
}
// Word-by-word typing
function typeTextWordByWord(el, cursor, text, idx, words, done) {
if (!words) words = text.split(" ");
if (idx < words.length) {
el.textContent += (idx === 0 ? "" : " ") + words[idx];
chatBox.scrollTop = chatBox.scrollHeight;
const id = setTimeout(
() => typeTextWordByWord(el, cursor, text, idx + 1, words, done),
typingSpeed
);
currentTimeouts.push(id);
} else {
cursor.remove();
done && done();
}
}
function playConversation(topic, i = 0) {
const set = conversations[topic];
if (!set) return;
const conversation = set[secaEnabled ? "on" : "off"];
if (!conversation || i >= conversation.length) return;
const msg = conversation[i];
const { textEl, cursor } = createMessageElement(msg.role, msg.label);
typeTextWordByWord(textEl, cursor, msg.text, 0, null, () => {
const id = setTimeout(() => playConversation(topic, i + 1), messageDelay);
currentTimeouts.push(id);
});
}
function startTopic(topic) {
clearAllTimeouts();
clearChat();
currentTopic = topic;
playConversation(topic);
}
// Dropdown behaviour
topicToggle.addEventListener("click", () => {
topicMenu.classList.toggle("open");
});
topicMenu.addEventListener("click", (e) => {
const item = e.target.closest(".dropdown-item");
if (!item) return;
const topic = item.getAttribute("data-topic");
if (!topic) return;
if (topic === "ml") topicLabel.textContent = "Machine Learning";
if (topic === "math") topicLabel.textContent = "Math";
if (topic === "history") topicLabel.textContent = "History";
topicMenu.classList.remove("open");
startTopic(topic);
});
// SECA toggle behaviour – now actually changes which conversation is used
secaToggle.addEventListener("click", () => {
secaEnabled = !secaEnabled;
secaToggle.classList.toggle("on", secaEnabled);
secaToggle.setAttribute("aria-pressed", secaEnabled ? "true" : "false");
secaLabel.textContent = secaEnabled ? "Use SECA" : "Use SECA";
startTopic(currentTopic);
});
// Close dropdown on outside click
document.addEventListener("click", (e) => {
if (!topicToggle.contains(e.target) && !topicMenu.contains(e.target)) {
topicMenu.classList.remove("open");
}
});
// Initial load
document.addEventListener("DOMContentLoaded", () => {
startTopic(currentTopic);
});
</script>