Complete graphical review and session manage for vers 1
This commit is contained in:
+15
-1
@@ -67,7 +67,21 @@ body.sessions-open .sessions-wrapper {
|
||||
box-shadow: 6px 0 20px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
/* rest of your existing chat styles unchanged... */
|
||||
.offcanvas-left {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: -300px; /* panel width */
|
||||
width: 300px;
|
||||
height: 100%;
|
||||
background: var(--bs-body-bg, #fff);
|
||||
box-shadow: 2px 0 5px rgba(0,0,0,0.3);
|
||||
transition: left 0.3s ease;
|
||||
z-index: 1050;
|
||||
}
|
||||
|
||||
.offcanvas-left.show {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
|
||||
/* Chat styles */
|
||||
|
||||
@@ -54,7 +54,7 @@ export default function ChatHeader({
|
||||
</div>
|
||||
|
||||
{/* Right column: theme toggle */}
|
||||
<div className="col-3 text-end">
|
||||
<div className="col-3 d-flex flex-row-reverse">
|
||||
<button
|
||||
className="btn btn-sm btn-outline-light d-flex align-items-center gap-1"
|
||||
onClick={onToggleTheme}
|
||||
|
||||
+29
-28
@@ -4,15 +4,6 @@ import ChatHeader from "./ChatHeader";
|
||||
import ChatWindow from "./ChatWindow";
|
||||
import ChatInput from "./ChatInput";
|
||||
import SessionTable from "./SessionTable";
|
||||
import { setSessionId, getUserId } from "./useSessionId";
|
||||
|
||||
function handleSelectSession(sessionId) {
|
||||
setSessionId(sessionId); // ✅ overwrite current
|
||||
const userId = getUserId();
|
||||
fetch(`/v1/history?user_id=${userId}&session_id=${sessionId}`)
|
||||
.then(res => res.json())
|
||||
.then(history => setMessages(history));
|
||||
}
|
||||
|
||||
export default function ChatLayout({
|
||||
theme,
|
||||
@@ -24,22 +15,38 @@ export default function ChatLayout({
|
||||
onReloadHistory,
|
||||
onFreshStart,
|
||||
onCreateSession,
|
||||
onEditSession,
|
||||
onSelectSession,
|
||||
userId
|
||||
}) {
|
||||
const [showOffcanvas, setShowOffcanvas] = useState(false);
|
||||
const [showSessionsPanel, setShowSessionsPanel] = useState(false);
|
||||
|
||||
// 1️⃣ helper at the top (inside the component is fine)
|
||||
function getScrollbarWidth() {
|
||||
return window.innerWidth - document.documentElement.clientWidth;
|
||||
}
|
||||
|
||||
// update body class when panel state changes
|
||||
useEffect(() => {
|
||||
document.body.classList.toggle("sessions-open", showSessionsPanel);
|
||||
}, [showSessionsPanel]);
|
||||
|
||||
const openSessionManager = () => setShowSessionsPanel(true);
|
||||
const closeSessionManager = () => setShowSessionsPanel(false);
|
||||
// 2️⃣ revised toggles
|
||||
const openSessionManager = () => {
|
||||
const scrollBarWidth = getScrollbarWidth();
|
||||
document.body.style.overflow = "hidden";
|
||||
document.body.style.paddingRight = `${scrollBarWidth}px`;
|
||||
setShowSessionsPanel(true);
|
||||
};
|
||||
|
||||
const closeSessionManager = () => {
|
||||
document.body.style.overflow = "";
|
||||
document.body.style.paddingRight = "";
|
||||
setShowSessionsPanel(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* SHIFTING CONTENT */}
|
||||
<div className={`sessions-wrapper d-flex flex-column vh-100 ${theme.bodyBg}`}>
|
||||
<ChatHeader
|
||||
theme={theme}
|
||||
@@ -47,7 +54,8 @@ export default function ChatLayout({
|
||||
onReloadHistory={onReloadHistory}
|
||||
onFreshStart={onFreshStart}
|
||||
onCreateSession={onCreateSession}
|
||||
onManageSession={openSessionManager}
|
||||
onEditSession={onEditSession}
|
||||
onManageSession={openSessionManager} // opens panel
|
||||
/>
|
||||
|
||||
<div className="flex-grow-1 overflow-auto">
|
||||
@@ -65,13 +73,6 @@ export default function ChatLayout({
|
||||
<ChatInput onSend={onSend} loading={loading} />
|
||||
</div>
|
||||
|
||||
{/* DARK OVERLAY */}
|
||||
<div
|
||||
className={`sessions-overlay ${showSessionsPanel ? "show" : ""}`}
|
||||
onClick={closeSessionManager}
|
||||
></div>
|
||||
|
||||
{/* FIXED OFFCANVAS */}
|
||||
<div
|
||||
className={`offcanvas offcanvas-start ${showSessionsPanel ? "show" : ""}`}
|
||||
tabIndex="-1"
|
||||
@@ -82,15 +83,15 @@ export default function ChatLayout({
|
||||
<button
|
||||
type="button"
|
||||
className="btn-close text-reset"
|
||||
onClick={closeSessionManager}
|
||||
onClick={closeSessionManager} // closes panel
|
||||
></button>
|
||||
</div>
|
||||
<div className="offcanvas-body small">
|
||||
<SessionTable
|
||||
userId={userId}
|
||||
onSelectSession={onSelectSession}
|
||||
onClosePanel={() => setShowOffcanvas(false)}
|
||||
/>
|
||||
<div className="offcanvas-body">
|
||||
<SessionTable
|
||||
userId={userId}
|
||||
onSelectSession={onSelectSession}
|
||||
onClosePanel={closeSessionManager}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -60,10 +60,10 @@ export default function SessionTable({ userId, onSelectSession, onClosePanel })
|
||||
<table className="table table-striped table-hover align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Session Name</th>
|
||||
<th></th>
|
||||
<th>Session</th>
|
||||
<th>Created</th>
|
||||
<th>Messages</th>
|
||||
<th>Size (bytes)</th>
|
||||
<th className="text-nowrap"># mess</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -77,6 +77,13 @@ export default function SessionTable({ userId, onSelectSession, onClosePanel })
|
||||
key={s.session_id}
|
||||
className={s.session_id === activeSessionId ? "table-primary" : ""}
|
||||
>
|
||||
<td className="text-end text-nowrap">
|
||||
<button
|
||||
className="btn btn-sm px-1 btn-outline-secondary me-1"
|
||||
onClick={() => startEditing(s)}
|
||||
title="Rename"
|
||||
>✏️</button>
|
||||
</td>
|
||||
<td>
|
||||
{editingSessionId === s.session_id ? (
|
||||
<input
|
||||
@@ -106,16 +113,12 @@ export default function SessionTable({ userId, onSelectSession, onClosePanel })
|
||||
)}
|
||||
</td>
|
||||
<td>{new Date(s.created_at).toLocaleString()}</td>
|
||||
<td>{s.message_count}</td>
|
||||
<td>{s.history_size_bytes}</td>
|
||||
<td className="text-end">
|
||||
<td title={s.history_size_bytes}>
|
||||
{s.message_count}
|
||||
</td>
|
||||
<td className="text-end text-nowrap">
|
||||
<button
|
||||
className="btn btn-sm btn-outline-secondary me-1"
|
||||
onClick={() => startEditing(s)}
|
||||
title="Rename"
|
||||
>✏️</button>
|
||||
<button
|
||||
className="btn btn-sm btn-outline-danger"
|
||||
className="btn btn-sm px-1 btn-outline-danger"
|
||||
onClick={() => deleteSession(s.session_id)}
|
||||
title="Delete"
|
||||
>🗑️</button>
|
||||
|
||||
@@ -3,18 +3,22 @@ import React from "react";
|
||||
|
||||
export default function UserMessage({ content, theme }) {
|
||||
return (
|
||||
<div className="mb-2 text-end">
|
||||
// This outer div aligns the *bubble* to the right
|
||||
<div className="mb-2 d-flex justify-content-end">
|
||||
<div
|
||||
className={`d-inline-block p-2 rounded ${theme.userBg}`}
|
||||
style={{ maxWidth: "95%" }}
|
||||
className={`p-2 rounded ${theme.userBg}`}
|
||||
style={{
|
||||
maxWidth: "95%",
|
||||
textAlign: "left", // ensures text inside is left-aligned
|
||||
}}
|
||||
>
|
||||
<pre
|
||||
className="m-0 p-0"
|
||||
style={{
|
||||
whiteSpace: "pre-wrap",
|
||||
fontFamily: "inherit",
|
||||
backgroundColor: "transparent", // kill default <pre> background
|
||||
color: "inherit", // match bubble text color
|
||||
backgroundColor: "transparent",
|
||||
color: "inherit",
|
||||
}}
|
||||
>
|
||||
{content}
|
||||
|
||||
Reference in New Issue
Block a user