fix: session persistence + h3 readability + outline error messages
Session issues fixed: - Removed auto-draft-only gate for showing unassigned sessions on new posts (now shows on any post that has no linked sessions) - Auto-link unassigned session to current post when user opens it - Added beforeunload/pagehide flush to persist messages before page close (prevents data loss from 700ms debounce not firing) - Added warning log when session loads with 0 messages for debugging UI fix: - Override WP editor h3 shrinkage (11px/uppercase → 15px/normal/white) - Fix h2/h4-h6 headings in response content for dark theme readability Outline error messages: - Separate empty response from parse failure with distinct actionable messages - Show model name + token count on empty response for debugging - Reassure user that parse failures are usually one-time issues
This commit is contained in:
@@ -1078,6 +1078,39 @@
|
||||
}
|
||||
}, [sanitizeMessagesForStorage]);
|
||||
|
||||
// Flush pending message persistence on page unload to prevent data loss
|
||||
React.useEffect(() => {
|
||||
const flushOnUnload = () => {
|
||||
if (messagesSaveTimeoutRef.current) {
|
||||
clearTimeout(messagesSaveTimeoutRef.current);
|
||||
}
|
||||
if (!currentSessionId || isHydratingSessionRef.current) {
|
||||
return;
|
||||
}
|
||||
const sanitized = sanitizeMessagesForStorage(messages);
|
||||
const serialized = JSON.stringify(sanitized);
|
||||
if (serialized === lastPersistedMessagesRef.current) {
|
||||
return;
|
||||
}
|
||||
// Synchronous XHR as last resort during unload (sendBeacon can't set headers)
|
||||
try {
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', `${wpAgenticWriter.apiUrl}/conversations/${currentSessionId}/messages`, false); // sync
|
||||
xhr.setRequestHeader('Content-Type', 'application/json');
|
||||
xhr.setRequestHeader('X-WP-Nonce', wpAgenticWriter.nonce);
|
||||
xhr.send(JSON.stringify({ messages: sanitized }));
|
||||
} catch (e) {
|
||||
// Best effort - ignore errors during unload
|
||||
}
|
||||
};
|
||||
window.addEventListener('beforeunload', flushOnUnload);
|
||||
window.addEventListener('pagehide', flushOnUnload);
|
||||
return () => {
|
||||
window.removeEventListener('beforeunload', flushOnUnload);
|
||||
window.removeEventListener('pagehide', flushOnUnload);
|
||||
};
|
||||
}, [currentSessionId, messages, sanitizeMessagesForStorage]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!currentSessionId) {
|
||||
return;
|
||||
@@ -1205,10 +1238,10 @@
|
||||
postSessions = Array.isArray(postData?.sessions) ? postData.sessions : [];
|
||||
}
|
||||
|
||||
// Auto-draft fallback:
|
||||
// if this auto-draft has no linked sessions yet, surface active carry-over sessions
|
||||
// (unassigned or still auto-draft) so users can continue unfinished work.
|
||||
if (postSessions.length === 0 && currentPostStatus === 'auto-draft') {
|
||||
// Fallback: if this post has no linked sessions, surface active unassigned sessions
|
||||
// so users can continue unfinished work from other tabs or prior page loads.
|
||||
// This covers auto-draft AND draft posts that haven't had a session linked yet.
|
||||
if (postSessions.length === 0) {
|
||||
const activeRes = await fetch(`${wpAgenticWriter.apiUrl}/conversations?status=active&limit=50`, {
|
||||
method: 'GET',
|
||||
headers,
|
||||
@@ -1219,7 +1252,8 @@
|
||||
unassignedSessions = allActive.filter((s) => {
|
||||
const pid = Number(s?.post_id || 0);
|
||||
const postStatus = String(s?.post_status || '').toLowerCase();
|
||||
return pid === 0 || postStatus === 'auto-draft';
|
||||
// Show sessions that are unassigned, or linked to auto-drafts/drafts
|
||||
return pid === 0 || postStatus === 'auto-draft' || postStatus === '';
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1281,10 +1315,30 @@
|
||||
isHydratingSessionRef.current = true;
|
||||
setCurrentSessionId(sessionId);
|
||||
const sessionMessages = Array.isArray(data?.messages) ? data.messages : [];
|
||||
|
||||
// If session has no messages but has a post_id, it may have been improperly persisted
|
||||
if (sessionMessages.length === 0) {
|
||||
wpawLog.warn('Session loaded with 0 messages:', sessionId);
|
||||
}
|
||||
|
||||
lastPersistedMessagesRef.current = JSON.stringify(sanitizeMessagesForStorage(sessionMessages));
|
||||
hydrateSessionStateFromMessages(sessionMessages);
|
||||
setMessages(sessionMessages);
|
||||
setShowWelcome(false);
|
||||
|
||||
// Auto-link unassigned session to current post for continuity
|
||||
const sessionPostId = Number(data?.post_id || 0);
|
||||
if (postId && postId > 0 && sessionPostId === 0) {
|
||||
fetch(`${wpAgenticWriter.apiUrl}/conversations/${sessionId}/link-post`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-WP-Nonce': wpAgenticWriter.nonce,
|
||||
},
|
||||
body: JSON.stringify({ postId: postId }),
|
||||
}).catch(() => {}); // Non-blocking
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
isHydratingSessionRef.current = false;
|
||||
}, 0);
|
||||
|
||||
Reference in New Issue
Block a user