Add user‑message collapse/expand feature

Implemented a per‑message collapse state that hides long user messages (>30 lines) with a “Show entire message / Collapse” button.
• Added collapsedUserMsgs map and helper collapseKeyFor to generate stable keys.
• Initialized or updated the collapse map whenever chat sessions or the active session change, preserving user toggles.
• Provided toggleUserMsgCollapse to switch the collapsed state.
• Updated the JSX to render the truncated content with an ellipsis when collapsed and render the expand/collapse button.
• Adjusted CSS for the new expand button: changed color to var(--muted), added align-self: flex-start, removed stray left margin, and retained bold, pointer cursor, and left‑aligned text.
• Minor style tweaks for the button hover effect.
This commit is contained in:
2025-08-26 05:05:43 +02:00
parent f5d383ec94
commit eb753582c1
2 changed files with 59 additions and 7 deletions

View File

@@ -352,6 +352,38 @@ export default function App() {
// Tip state: { [sessionId]: messageId }
const [newMsgTip, setNewMsgTip] = useState({});
// Collapse state per user message: { [msgKey]: boolean } — true means "collapsed"
const [collapsedUserMsgs, setCollapsedUserMsgs] = useState({});
// Compute a stable key for collapse map (prefer id, else session:index)
const collapseKeyFor = (m, i, sessionId) => (m?.id ? m.id : `${sessionId}:${i}`);
// Initialize/maintain collapsed map whenever messages or the active session change
useEffect(() => {
if (!activeSessionId) return;
const msgs =
(chatSessions.find(s => s.session_id === activeSessionId)?.messages) || [];
setCollapsedUserMsgs(prev => {
const next = {};
msgs.forEach((m, i) => {
if (m.role !== 'user') return;
const key = collapseKeyFor(m, i, activeSessionId);
const lineCount = (m.content || '').split(/\r\n|\r|\n/).length;
const needsCollapse = lineCount > 30;
// Default collapsed = true when needsCollapse; preserve user toggles
next[key] = needsCollapse ? (prev[key] ?? true) : false;
});
return next;
});
}, [chatSessions, activeSessionId]);
// Toggle collapse/expand for a specific message
function toggleUserMsgCollapse(key) {
setCollapsedUserMsgs(prev => ({ ...prev, [key]: !(prev[key] ?? true) }));
}
const setUserScrolledUp = React.useCallback((sessionId, value) => {
setUserScrolledUpState(prev => {
const next = { ...prev, [sessionId]: value };
@@ -1181,12 +1213,10 @@ export default function App() {
<div className="user-message-wrapper">
{isEditingThis ? (
<div className="msg-content msg-content--user editing">
{/* Shadow defines bubble width/height based on current text */}
<div className="user-edit-shadow" aria-hidden="true">
{editText}
</div>
{/* Overlay textarea fills the bubble exactly */}
<TextareaAutosize
className="edit-message-input edit-overlay"
value={editText}
@@ -1201,7 +1231,29 @@ export default function App() {
/>
</div>
) : (
<div className="msg-content msg-content--user">{m.content}</div>
(() => {
const raw = m.content || '';
const lines = raw.split(/\r\n|\r|\n/);
const needsCollapse = lines.length > 30;
const key = collapseKeyFor(m, i, activeSessionId);
const isCollapsed = needsCollapse ? (collapsedUserMsgs[key] ?? true) : false;
const displayText = isCollapsed ? lines.slice(0, 30).join('\n') + '\n…' : raw;
return (
<>
<div className="msg-content msg-content--user">{displayText}</div>
{needsCollapse && (
<button
className="user-msg-expand"
onClick={() => toggleUserMsgCollapse(key)}
aria-expanded={isCollapsed ? 'false' : 'true'}
>
{isCollapsed ? 'Show entire message' : 'Collapse'}
</button>
)}
</>
);
})()
)}
{!isSending && !isEditingThis && (
<div className="message-options-bar user-options">

View File

@@ -351,15 +351,15 @@ textarea.input {
padding: 0;
background: transparent;
border: none;
color: var(--accent);
color: var(--muted);
font-weight: 700; /* bold as requested */
cursor: pointer;
text-align: left; /* align with text content */
align-self: flex-start; /* <-- key line */
margin-left: 0; /* ensure no stray left margin */
text-align: left; /* you already had this */
}
.msg.user .user-msg-expand:hover {
opacity: .9;
text-decoration: underline;
}
.msg.assistant {