IMPROVEMENT: page switching system based on navbar now works

This commit is contained in:
Frederico @ VilaRosa02 2025-09-18 10:15:48 +00:00
parent 00fa5ae75a
commit d56ac63c43
5 changed files with 151 additions and 54 deletions

1
files
View File

@ -1 +1,2 @@
index.html
main.js

View File

@ -11,7 +11,7 @@
<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>
<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"
@ -20,12 +20,12 @@
</button>
<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">
<a class="nav-link active" href="#chat">Chat</a>
<a class="nav-link active" href="#chatPage" data-page="chatPage">Chat</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#systemAcc">System</a>
<a class="nav-link" href="#systemAcc" data-page="systemAcc">System</a>
</li>
</ul>
@ -37,58 +37,63 @@
</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 id="pageWrapper" class="tab-content">
<div class="tab-pane page" id="systemAcc" role="tabpanel">
<div class="accordion mb-3">
<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 class="time"><?= date('H:i', $ts) ?></div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
</div><!-- /#systemAcc -->
<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 id="chatPage" class="tab-pane page active show">
<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><!-- /.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>
<script>

View File

@ -209,3 +209,18 @@ html:not([data-theme="light"]) .btn-outline-light {
html:not([data-theme="light"]) .btn-outline-light:hover {
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
View File

@ -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');
}
})();

2
ticket
View File

@ -1 +1 @@
give me a drop-in replacement that uses jQuery
fix the "page switching" system. make it pure javascript. all pages are loaded in DOM/HTML but hidden. use a vanilla js system. when user clicks on navbar item, page fades and switches.