diff --git a/resources/js/chat-room.js b/resources/js/chat-room.js
index 765923d..5518539 100644
--- a/resources/js/chat-room.js
+++ b/resources/js/chat-room.js
@@ -64,8 +64,10 @@ export { bindChatToast } from "./chat-room/toast.js";
export { bindChatComposerControls, setChatComposerAction } from "./chat-room/composer.js";
export {
isExpiredChatImageMessage,
+ isAutoScrollEnabled,
localClearScreen,
scrollChatToBottom,
+ setAutoScrollEnabled,
syncAutoScrollControls,
toggleAutoScroll,
} from "./chat-room/message-utils.js";
@@ -319,8 +321,10 @@ if (typeof window !== "undefined") {
bindChatComposerControls,
setChatComposerAction,
isExpiredChatImageMessage,
+ isAutoScrollEnabled,
localClearScreen,
scrollChatToBottom,
+ setAutoScrollEnabled,
syncAutoScrollControls,
toggleAutoScroll,
bindInstantHoverTooltip,
@@ -592,8 +596,10 @@ if (typeof window !== "undefined") {
// ── 静态核心模块 window 挂载 ──
window.escapeHtml = escapeHtml;
window.isExpiredChatImageMessage = isExpiredChatImageMessage;
+ window.isChatAutoScrollEnabled = isAutoScrollEnabled;
window.localClearScreen = localClearScreen;
window.normalizeSafeChatUrl = normalizeSafeChatUrl;
+ window.setChatAutoScrollEnabled = setAutoScrollEnabled;
window.setAction = setChatComposerAction;
window.syncAutoScrollControls = syncAutoScrollControls;
diff --git a/resources/js/chat-room/appointment-announcement.js b/resources/js/chat-room/appointment-announcement.js
index dbd4c7c..8b5a320 100644
--- a/resources/js/chat-room/appointment-announcement.js
+++ b/resources/js/chat-room/appointment-announcement.js
@@ -1,6 +1,7 @@
// 聊天室任命/撤销公告监听,负责渲染大卡片和频道内系统提示。
import { escapeHtml } from "./html.js";
+import { isAutoScrollEnabled, scrollChatToBottom } from "./message-utils.js";
const APPOINTMENT_PHRASES = [
"望再接再厉,大展宏图,为大家服务!",
@@ -132,7 +133,7 @@ function appendAppointmentMessage(container, message) {
}
container.appendChild(message);
- container.scrollTop = container.scrollHeight;
+ scrollChatToBottom(container, isAutoScrollEnabled);
}
/**
diff --git a/resources/js/chat-room/chat-events.js b/resources/js/chat-room/chat-events.js
index b47b7bc..5dab2f3 100644
--- a/resources/js/chat-room/chat-events.js
+++ b/resources/js/chat-room/chat-events.js
@@ -4,6 +4,7 @@
import { escapeHtml, normalizeSafeChatUrl } from "./html.js";
import { normalizeDailyStatus } from "./preferences-status.js";
import { enqueueChatMessage } from "./message-renderer.js";
+import { isAutoScrollEnabled, scrollChatToBottom } from "./message-utils.js";
// ── 事件注册标记 ──
let chatEventsBound = false;
@@ -19,6 +20,16 @@ function getState() {
return window.chatState;
}
+/**
+ * 在开启自动滚屏时把指定聊天窗格滚动到底部。
+ *
+ * @param {HTMLElement|null|undefined} container 聊天消息容器
+ * @returns {void}
+ */
+function scrollWhenEnabled(container) {
+ scrollChatToBottom(container, isAutoScrollEnabled);
+}
+
/**
* 启动 WebSocket 初始化(DOMContentLoaded 之后调用)。
*/
@@ -79,7 +90,7 @@ function handleMutedEvent(e) {
: (state?.container);
if (targetContainer) {
targetContainer.appendChild(div);
- targetContainer.scrollTop = targetContainer.scrollHeight;
+ scrollWhenEnabled(targetContainer);
}
if (isMe && d.mute_time > 0) {
@@ -99,7 +110,7 @@ function handleMutedEvent(e) {
const say2 = document.getElementById("say2");
if (say2) {
say2.appendChild(unmuteDiv);
- say2.scrollTop = say2.scrollHeight;
+ scrollWhenEnabled(say2);
}
}, d.mute_time * 60 * 1000);
}
@@ -148,7 +159,7 @@ function setupScreenClearedListener() {
sysDiv.innerHTML = `🧹 管理员 ${safeOperator} 已执行全员清屏(${timeStr})`;
if (say1) {
say1.appendChild(sysDiv);
- say1.scrollTop = say1.scrollHeight;
+ scrollWhenEnabled(say1);
}
});
}
@@ -205,7 +216,7 @@ function setupChangelogPublishedListener() {
const say1 = document.getElementById("chat-messages-container");
if (say1) {
say1.appendChild(sysDiv);
- say1.scrollTop = say1.scrollHeight;
+ scrollWhenEnabled(say1);
}
});
}
@@ -255,7 +266,7 @@ function setupGomokuInviteListener() {
const say1 = document.getElementById("chat-messages-container");
if (say1) {
say1.appendChild(div);
- say1.scrollTop = say1.scrollHeight;
+ scrollWhenEnabled(say1);
}
if (!isSelf) {
@@ -294,7 +305,7 @@ function setupGomokuInviteListener() {
const say1 = document.getElementById("chat-messages-container");
if (say1) {
say1.appendChild(div);
- say1.scrollTop = say1.scrollHeight;
+ scrollWhenEnabled(say1);
}
});
}
diff --git a/resources/js/chat-room/composer.js b/resources/js/chat-room/composer.js
index 3def3c8..f5f4dea 100644
--- a/resources/js/chat-room/composer.js
+++ b/resources/js/chat-room/composer.js
@@ -1,6 +1,8 @@
// 聊天输入区完整逻辑:发送消息、草稿管理、IME 防重、神秘箱子暗号拦截。
// 从 Blade 内联脚本 scripts.blade.php 迁移至 Vite 模块。
+import { isAutoScrollEnabled, scrollChatToBottom } from "./message-utils.js";
+
let chatComposerEventsBound = false;
function csrf() {
@@ -165,7 +167,7 @@ async function sendMessage(e) {
const say2 = document.getElementById("say2");
if (say2) {
say2.appendChild(muteDiv);
- say2.scrollTop = say2.scrollHeight;
+ scrollChatToBottom(say2, isAutoScrollEnabled);
}
if (state) {
state.isSending = false;
diff --git a/resources/js/chat-room/marriage-modals.js b/resources/js/chat-room/marriage-modals.js
index 66bbe7e..6eeaf99 100644
--- a/resources/js/chat-room/marriage-modals.js
+++ b/resources/js/chat-room/marriage-modals.js
@@ -1,5 +1,7 @@
// 婚姻弹窗辅助入口,承接从 marriage-modals.blade.php 迁移出的全局函数。
+import { isAutoScrollEnabled, scrollChatToBottom } from "./message-utils.js";
+
/**
* 向聊天主窗口追加一条婚姻系统公告,允许传入受控 HTML 按钮。
*
@@ -17,7 +19,7 @@ export function appendSystemMessage(html) {
div.style.cssText = "background:linear-gradient(135deg,#fdf4ff,#fce7f3); border-left:3px solid #ec4899; border-radius:6px; padding:5px 12px; margin:3px 0; font-size:13px; line-height:1.6;";
div.innerHTML = `${html}`;
container.appendChild(div);
- container.scrollTop = container.scrollHeight;
+ scrollChatToBottom(container, isAutoScrollEnabled);
}
/**
diff --git a/resources/js/chat-room/message-utils.js b/resources/js/chat-room/message-utils.js
index 0103e84..8f9a998 100644
--- a/resources/js/chat-room/message-utils.js
+++ b/resources/js/chat-room/message-utils.js
@@ -75,10 +75,46 @@ export function localClearScreen(roomId = window.chatContext?.roomId, maxMessage
if (publicPane) {
publicPane.appendChild(notice);
- publicPane.scrollTop = publicPane.scrollHeight;
+ scrollChatToBottom(publicPane, isAutoScrollEnabled);
}
}
+/**
+ * 读取当前是否允许聊天窗口自动滚屏。
+ *
+ * @returns {boolean}
+ */
+export function isAutoScrollEnabled() {
+ const state = window.chatState;
+
+ if (state && typeof state.autoScroll !== "undefined") {
+ return Boolean(state.autoScroll);
+ }
+
+ const checkbox = document.getElementById("auto_scroll");
+
+ return checkbox ? Boolean(checkbox.checked) : true;
+}
+
+/**
+ * 写入自动滚屏状态,并同步页面复选框和状态文字。
+ *
+ * @param {boolean} enabled 是否开启自动滚屏
+ * @returns {boolean}
+ */
+export function setAutoScrollEnabled(enabled) {
+ const nextEnabled = Boolean(enabled);
+ const state = window.chatState;
+
+ if (state) {
+ state.autoScroll = nextEnabled;
+ }
+
+ syncAutoScrollControls(nextEnabled);
+
+ return nextEnabled;
+}
+
/**
* 同步自动滚屏复选框与状态文字。
*
@@ -111,6 +147,8 @@ export function toggleAutoScroll(getCurrent, setCurrent) {
if (typeof setCurrent === "function") {
setCurrent(nextEnabled);
+ } else {
+ setAutoScrollEnabled(nextEnabled);
}
syncAutoScrollControls(nextEnabled);