IMPROVEMENT: page switching system based on navbar now works
This commit is contained in:
parent
00fa5ae75a
commit
d56ac63c43
111
index.html
111
index.html
@ -11,7 +11,7 @@
|
|||||||
<body>
|
<body>
|
||||||
<div class="container py-4 chat-wrap">
|
<div class="container py-4 chat-wrap">
|
||||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark rounded mb-3 px-3">
|
<nav class="navbar navbar-expand-lg navbar-dark bg-dark rounded mb-3 px-3">
|
||||||
<a class="navbar-brand" href="#">FMF GPT</a>
|
<a class="navbar-brand" href="#">FMF-GPT</a>
|
||||||
|
|
||||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
|
||||||
data-bs-target="#topNav" aria-controls="topNav"
|
data-bs-target="#topNav" aria-controls="topNav"
|
||||||
@ -20,12 +20,12 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div class="collapse navbar-collapse" id="topNav">
|
<div class="collapse navbar-collapse" id="topNav">
|
||||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
<ul class="navbar-nav me-auto mb-2 mb-lg-0 nav-tab">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link active" href="#chat">Chat</a>
|
<a class="nav-link active" href="#chatPage" data-page="chatPage">Chat</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="#systemAcc">System</a>
|
<a class="nav-link" href="#systemAcc" data-page="systemAcc">System</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
@ -37,58 +37,63 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
<div id="pageWrapper" class="tab-content">
|
||||||
<div class="accordion mb-3" id="systemAcc">
|
<div class="tab-pane page" id="systemAcc" role="tabpanel">
|
||||||
<div class="accordion-item">
|
<div class="accordion mb-3">
|
||||||
<h2 class="accordion-header" id="headingOne">
|
<div class="accordion-item">
|
||||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="false" aria-controls="collapseOne">
|
<h2 class="accordion-header" id="headingOne">
|
||||||
System prompt (optional)
|
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="false" aria-controls="collapseOne">
|
||||||
</button>
|
System prompt (optional)
|
||||||
</h2>
|
</button>
|
||||||
<div id="collapseOne" class="accordion-collapse collapse" aria-labelledby="headingOne" data-bs-parent="#systemAcc">
|
</h2>
|
||||||
<div class="accordion-body">
|
<div id="collapseOne" class="accordion-collapse collapse" aria-labelledby="headingOne" data-bs-parent="#systemAcc">
|
||||||
<textarea id="systemInput" class="form-control" rows="3" placeholder="You are a concise assistant that..."><?= $systemPrefill ?></textarea>
|
<div class="accordion-body">
|
||||||
<div class="form-text">Saved in the session and applied to each message until changed.</div>
|
<textarea id="systemInput" class="form-control" rows="3" placeholder="You are a concise assistant that..."><?= $systemPrefill ?></textarea>
|
||||||
</div>
|
<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>
|
||||||
<div class="time"><?= date('H:i', $ts) ?></div>
|
|
||||||
</div>
|
</div>
|
||||||
<?php endforeach; ?>
|
</div>
|
||||||
<?php endif; ?>
|
</div><!-- /#systemAcc -->
|
||||||
</div>
|
|
||||||
|
|
||||||
<form id="chatForm" class="card shadow-sm">
|
<div id="chatPage" class="tab-pane page active show">
|
||||||
<div class="card-body">
|
<div id="chat" class="chat-box p-3 mb-3 text-white">
|
||||||
<div class="mb-2">
|
<?php if (!$history): ?>
|
||||||
<textarea id="messageInput" class="form-control" rows="3" placeholder="Ask something… (Shift+Enter for newline)" required></textarea>
|
<div class="text-secondary">Start the conversation below…</div>
|
||||||
</div>
|
<?php else: ?>
|
||||||
<div class="d-flex gap-2">
|
<?php foreach ($history as $m): ?>
|
||||||
<button id="sendBtn" type="submit" class="btn btn-primary">Send</button>
|
<?php
|
||||||
</div>
|
$role = htmlspecialchars($m['role'] ?? '', ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
|
||||||
</div>
|
$ts = (int)($m['ts'] ?? time());
|
||||||
</form>
|
?>
|
||||||
|
<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><!-- /.chat-box -->
|
||||||
|
|
||||||
|
<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><!-- /#chatForm -->
|
||||||
|
</div><!-- /#chat -->
|
||||||
|
</div><!-- /#pageWrapper -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
15
main.css
15
main.css
@ -209,3 +209,18 @@ html:not([data-theme="light"]) .btn-outline-light {
|
|||||||
html:not([data-theme="light"]) .btn-outline-light:hover {
|
html:not([data-theme="light"]) .btn-outline-light:hover {
|
||||||
background-color: rgba(255,255,255,0.12) !important;
|
background-color: rgba(255,255,255,0.12) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Vanilla pages system (fixed) */
|
||||||
|
.tab-content .page {
|
||||||
|
display: none;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 200ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-content .page.active {
|
||||||
|
display: block; /* stays at opacity: 0 until .show is added */
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-content .page.show {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|||||||
76
main.js
76
main.js
@ -110,3 +110,79 @@ $(function () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ========= Vanilla JS Page Switching (fixed) =========
|
||||||
|
(function () {
|
||||||
|
const navLinks = document.querySelectorAll('.nav-tab .nav-link[data-page]');
|
||||||
|
const pages = document.querySelectorAll('#pageWrapper .page');
|
||||||
|
const DURATION = 200; // keep in sync with CSS
|
||||||
|
|
||||||
|
function getActivePage() {
|
||||||
|
return document.querySelector('#pageWrapper .page.active');
|
||||||
|
}
|
||||||
|
|
||||||
|
function setActiveNav(targetId) {
|
||||||
|
navLinks.forEach(a => {
|
||||||
|
const isActive = a.getAttribute('data-page') === targetId;
|
||||||
|
a.classList.toggle('active', isActive);
|
||||||
|
a.setAttribute('aria-current', isActive ? 'page' : 'false');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function showPage(targetId) {
|
||||||
|
const current = getActivePage();
|
||||||
|
const next = document.getElementById(targetId);
|
||||||
|
if (!next || current === next) return;
|
||||||
|
|
||||||
|
// Prepare next: visible container, but still transparent
|
||||||
|
next.classList.add('active'); // display:block; opacity:0
|
||||||
|
|
||||||
|
// Fade-in next on the next frame
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
next.classList.add('show'); // opacity -> 1 (transition)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fade-out current (if any)
|
||||||
|
if (current) {
|
||||||
|
current.classList.remove('show'); // opacity -> 0 (transition)
|
||||||
|
|
||||||
|
let cleaned = false;
|
||||||
|
const cleanup = () => {
|
||||||
|
if (cleaned) return;
|
||||||
|
cleaned = true;
|
||||||
|
current.classList.remove('active');
|
||||||
|
current.removeEventListener('transitionend', onEnd);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onEnd = (e) => {
|
||||||
|
if (e.target === current && e.propertyName === 'opacity') {
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
current.addEventListener('transitionend', onEnd);
|
||||||
|
// Fallback in case transitionend doesn't fire (reduced motion, etc.)
|
||||||
|
setTimeout(cleanup, DURATION + 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
setActiveNav(targetId);
|
||||||
|
history.replaceState(null, '', '#' + targetId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Click handlers
|
||||||
|
navLinks.forEach(link => {
|
||||||
|
link.addEventListener('click', (ev) => {
|
||||||
|
ev.preventDefault();
|
||||||
|
const targetId = link.getAttribute('data-page');
|
||||||
|
showPage(targetId);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initial page from hash
|
||||||
|
const initial = (location.hash || '#chat').slice(1);
|
||||||
|
if (initial !== 'chat' && document.getElementById(initial)) {
|
||||||
|
showPage(initial);
|
||||||
|
} else {
|
||||||
|
setActiveNav('chat');
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user