Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion application/single_app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@
EXECUTOR_TYPE = 'thread'
EXECUTOR_MAX_WORKERS = 30
SESSION_TYPE = 'filesystem'
VERSION = "0.241.008"
VERSION = "0.241.007"

SECRET_KEY = os.getenv('SECRET_KEY', 'dev-secret-key-change-in-production')

Expand Down
70 changes: 4 additions & 66 deletions application/single_app/static/js/chat/chat-global.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,71 +9,9 @@ let groupPrompts = [];
let publicPrompts = [];
let currentlyEditingId = null;

function getChatScrollContainer() {
return (
document.getElementById("chat-messages-container") ||
document.getElementById("chatbox") ||
null
);
}

function isChatNearBottom(threshold = 40) {
const container = getChatScrollContainer();
if (!container) return true;

const distanceFromBottom =
container.scrollHeight - (container.scrollTop + container.clientHeight);
return distanceFromBottom <= threshold;
}

function scrollChatToBottom() {
const container = getChatScrollContainer();
if (container) {
container.scrollTop = container.scrollHeight;
const chatbox = document.getElementById("chatbox");
if (chatbox) {
chatbox.scrollTop = chatbox.scrollHeight;
}
}

function showScrollToBottomButton() {
const btn = document.getElementById("scroll-to-bottom-btn");
if (!btn) return;
btn.classList.remove("d-none");
}

function hideScrollToBottomButton() {
const btn = document.getElementById("scroll-to-bottom-btn");
if (!btn) return;
btn.classList.add("d-none");
}

function initializeChatScrollBehavior() {
const container = getChatScrollContainer();
const btn = document.getElementById("scroll-to-bottom-btn");

if (!container) return;

// Initial state
if (isChatNearBottom()) {
hideScrollToBottomButton();
} else {
showScrollToBottomButton();
}

container.addEventListener("scroll", () => {
if (isChatNearBottom()) {
hideScrollToBottomButton();
} else {
showScrollToBottomButton();
}
});

if (btn) {
btn.addEventListener("click", () => {
scrollChatToBottom();
hideScrollToBottomButton();
});
}
}

window.addEventListener("load", () => {
initializeChatScrollBehavior();
});
}
49 changes: 4 additions & 45 deletions application/single_app/static/js/chat/chat-messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -1036,14 +1036,7 @@
});
}

// For AI messages, only auto-scroll if the user is currently near
// the bottom. This prevents a final jump after a long answer if
// the user has scrolled up to read earlier content.
if (typeof isChatNearBottom === 'function' && typeof scrollChatToBottom === 'function') {
if (isChatNearBottom()) {
scrollChatToBottom();
}
}
scrollChatToBottom();
return; // <<< EXIT EARLY FOR AI MESSAGES

// --- Handle ALL OTHER message types ---
Expand Down Expand Up @@ -1113,10 +1106,7 @@

// Validate image URL before creating img tag
if (messageContent && messageContent !== 'null' && messageContent.trim() !== '') {
// Use a placeholder container; the actual <img> element will be
// created with DOM APIs after insertion to avoid string-based
// attribute interpolation in src/data-*.
messageContentHtml = '<span class="generated-image-placeholder"></span>';
messageContentHtml = `<img src="${messageContent}" alt="${isUserUpload ? 'Uploaded' : 'Generated'} Image" class="generated-image" style="width: 170px; height: 170px; cursor: pointer;" data-image-src="${messageContent}" onload="scrollChatToBottom()" onerror="this.src='/static/images/image-error.png'; this.alt='Failed to load image';" />`;

Check failure on line 1109 in application/single_app/static/js/chat/chat-messages.js

View workflow job for this annotation

GitHub Actions / xss-sink-check

Avoid interpolating untrusted values directly into href/src/title/style/data-* attributes. Prefer DOM APIs and explicit URL normalization, or add 'xss-check: ignore' with a justification.

Check failure on line 1109 in application/single_app/static/js/chat/chat-messages.js

View workflow job for this annotation

GitHub Actions / xss-sink-check

Avoid inline event-handler attributes in rendered HTML. Use addEventListener or data-* hooks, or add 'xss-check: ignore' with a justification.
} else {
messageContentHtml = `<div class="alert alert-warning"><i class="bi bi-exclamation-triangle me-2"></i>Failed to ${isUserUpload ? 'load' : 'generate'} image - invalid response from image service</div>`;
}
Expand Down Expand Up @@ -1266,28 +1256,6 @@
// Append and scroll (common actions for non-AI)
chatbox.appendChild(messageDiv);

// Attach safe image element and error handler for generated/uploaded images
if (sender === "image") {
const placeholder = messageDiv.querySelector('.generated-image-placeholder');
if (placeholder && messageContent && messageContent !== 'null' && messageContent.trim() !== '') {
const imgEl = document.createElement('img');
imgEl.className = 'generated-image';
imgEl.style.width = '170px';
imgEl.style.height = '170px';
imgEl.style.cursor = 'pointer';
imgEl.src = messageContent;
imgEl.alt = isUserUpload ? 'Uploaded Image' : 'Generated Image';
imgEl.dataset.imageSrc = messageContent;

imgEl.addEventListener('error', () => {
imgEl.src = '/static/images/image-error.png';
imgEl.alt = 'Failed to load image';
});

placeholder.replaceWith(imgEl);
}
}

// Highlight code blocks in the messages
messageDiv.querySelectorAll('pre code[class^="language-"]').forEach((block) => {
const match = block.className.match(/language-([a-zA-Z0-9]+)/);
Expand Down Expand Up @@ -1410,16 +1378,7 @@
}
}

// For new user/file/image messages, scroll to bottom once so the
// user sees what they just sent. For history loads, only scroll
// if they are already near the bottom.
if (isNewMessage && typeof scrollChatToBottom === 'function') {
scrollChatToBottom();
} else if (typeof isChatNearBottom === 'function' && typeof scrollChatToBottom === 'function') {
if (isChatNearBottom()) {
scrollChatToBottom();
}
}
scrollChatToBottom();
} // End of the large 'else' block for non-AI messages
}

Expand Down Expand Up @@ -1477,7 +1436,7 @@
const tempUserMessageId = `temp_user_${Date.now()}`;

// Append user message first with temporary ID
appendMessage("You", finalMessageToSend, null, tempUserMessageId, false, [], [], [], null, null, null, true);
appendMessage("You", finalMessageToSend, null, tempUserMessageId);
userInput.value = "";
userInput.style.height = "";
// Update send button visibility after clearing input
Expand Down
16 changes: 2 additions & 14 deletions application/single_app/templates/chats.html
Original file line number Diff line number Diff line change
Expand Up @@ -267,20 +267,8 @@ <h5 id="current-conversation-title" class="mb-0 flex-grow-1 text-truncate">
</div>
</div>

<div id="chat-messages-container" class="position-relative flex-grow-1" style="overflow-y: auto;">
<div id="chatbox" class="flex-grow-1 p-3">
<!-- Chat messages will be dynamically loaded here -->
</div>
<button
id="scroll-to-bottom-btn"
type="button"
class="btn btn-primary rounded-circle position-sticky d-none"
style="margin-left: auto; bottom: 1rem; width: 40px; height: 40px; display: flex; align-items: center; justify-content: center;"
aria-label="Scroll to latest message"
title="Scroll to latest message"
>
<i class="bi bi-arrow-down"></i>
</button>
<div id="chatbox" class="flex-grow-1 p-3" style="overflow-y: auto;">
<!-- Chat messages will be dynamically loaded here -->
</div>

<div class="p-3 border-top flex-shrink-0">
Expand Down
10 changes: 0 additions & 10 deletions docs/explanation/release_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,6 @@ This page tracks notable Simple Chat releases and organizes the detailed change

For feature-focused and fix-focused drill-downs by version, see [Features by Version](/explanation/features/) and [Fixes by Version](/explanation/fixes/).

### **(v0.241.008)**

#### User Interface Enhancements

* **Chat Scroll Behavior and 508 Usability**
* Updated chat message rendering so the viewport no longer jumps to the very bottom of long assistant responses when they finish loading while the user is reading near the top.
* Auto-scroll now only occurs when the user is already near the bottom of the conversation, and a floating "scroll to latest message" button appears when new content arrives below the current view.
* This aligns the chat experience more closely with other AI chat tools and reduces unexpected motion for 508 testers and keyboard users.
* (Ref: `chats.html`, `chat-global.js`, `chat-messages.js`)

### **(v0.241.007)**

## New Feature
Expand Down
Loading