// 聊天室字号偏好控制,保留旧的 localStorage key 以兼容已有用户设置。 export const CHAT_FONT_SIZE_STORAGE_KEY = "chat_font_size"; export const CHAT_DEFAULT_FONT_SIZE = 13; export const CHAT_FONT_SIZE_MIN = 10; export const CHAT_FONT_SIZE_MAX = 30; let fontSizeEventsBound = false; /** * 规整聊天室字号,过滤非法或越界的旧缓存值。 * * @param {unknown} size 字号大小 * @returns {number|null} */ export function normalizeChatFontSize(size) { const px = Number.parseInt(String(size ?? ""), 10); if (Number.isNaN(px) || px < CHAT_FONT_SIZE_MIN || px > CHAT_FONT_SIZE_MAX) { return null; } return px; } /** * 同步底部输入框上方工具按钮字号。 * * @param {number} px 用户选择的聊天字号 * @returns {void} */ function applyInputToolbarFontSize(px) { const toolbarRow = document.querySelector("#chat-form > .input-row"); if (!(toolbarRow instanceof HTMLElement)) { return; } const toolbarFontSize = Math.max(1, px - 1); const fontSize = `${toolbarFontSize}px`; toolbarRow.style.fontSize = fontSize; toolbarRow.style.fontFamily = "inherit"; toolbarRow.style.lineHeight = "1.2"; toolbarRow.querySelectorAll([ ":scope > label", ":scope > label select", ":scope > label input", ":scope > button", ":scope > div > button", "#feature-menu button", "#admin-menu button", ].join(",")).forEach((control) => { control.style.fontFamily = "inherit"; control.style.fontSize = "inherit"; control.style.lineHeight = "1.2"; control.style.fontWeight = "400"; }); } /** * 应用字号到聊天消息窗口和输入栏工具按钮,并保存到 localStorage。 * * @param {string|number} size 字号大小 * @param {{syncContext?:boolean}} options 同步选项 * @returns {boolean} */ export function applyFontSize(size, options = {}) { const px = normalizeChatFontSize(size); if (px === null) { return false; } const publicContainer = document.getElementById("chat-messages-container"); const privateContainer = document.getElementById("chat-messages-container2"); if (publicContainer) { publicContainer.style.fontSize = `${px}px`; } if (privateContainer) { privateContainer.style.fontSize = `${px}px`; } applyInputToolbarFontSize(px); localStorage.setItem(CHAT_FONT_SIZE_STORAGE_KEY, String(px)); if (options.syncContext !== false && window.chatContext && typeof window.chatContext === "object") { window.chatContext.chatPreferences = { ...(window.chatContext.chatPreferences || {}), font_size: px, }; } const selector = document.getElementById("font_size_select"); if (selector) { selector.value = String(px); } return true; } /** * 从账号偏好或 localStorage 恢复已保存的聊天室字号。 * * @returns {boolean} */ export function restoreChatFontSize() { const serverFontSize = normalizeChatFontSize(window.chatContext?.chatPreferences?.font_size); const localFontSize = normalizeChatFontSize(localStorage.getItem(CHAT_FONT_SIZE_STORAGE_KEY)); const saved = serverFontSize ?? localFontSize ?? CHAT_DEFAULT_FONT_SIZE; return applyFontSize(saved, { syncContext: serverFontSize !== null }); } /** * 绑定字号选择器事件。 * * @returns {void} */ export function bindChatFontSizeControl() { if (fontSizeEventsBound || typeof document === "undefined") { return; } fontSizeEventsBound = true; document.addEventListener("change", (event) => { if (!(event.target instanceof HTMLSelectElement) || event.target.id !== "font_size_select") { return; } if (applyFontSize(event.target.value)) { void window.saveChatPreferences?.(); } }); }