refactor(slash-commands): 优化 /goto-floor 命令并添加高亮功能 - 重新组织消息加载和滚动逻辑,提高命令成功率 - 添加消息元素高亮功能,使用 flashHighlight 或临时 CSS 类

ee11f021ebfae786dc1be56b9ef02a77c9f05642

awaae001 <3271436144@qq.com>

1 files changed, +45 -84Showing whitespace changes
public/scripts/slash-commands.js+45 -84
@@ -1,4 +1,5 @@
1import { Fuse, DOMPurify } from '../lib.js';1import { Fuse, DOMPurify } from '../lib.js';
2import { flashHighlight } from './utils.js';
23
3import {4import {
4 Generate,5 Generate,
@@ -2134,64 +2135,67 @@ export function initDefaultSlashCommands() {
2134 name: 'goto-floor',2135 name: 'goto-floor',
2135 aliases: ['floor', 'jump', 'scrollto'],2136 aliases: ['floor', 'jump', 'scrollto'],
2136 callback: async (_, index) => {2137 callback: async (_, index) => {
2137 // --- Load all messages first to ensure the target element exists ---
2138 console.log(`INFO: Loading all messages before attempting to goto-floor ${index}.`);
2139 await showMoreMessages(Number.MAX_SAFE_INTEGER);
2140 console.log(`INFO: All messages loaded (or loading initiated).`);
2141 // --- End of loading step ---
2142
2143
2144 const floorIndex = Number(index);2138 const floorIndex = Number(index);
2145 2139
2146 // Validate input2140 const chatLength = typeof chat !== 'undefined' ? chat.length : -1; // Use -1 if chat is undefined to avoid errors
2147 if (isNaN(floorIndex) || floorIndex < 0 || (typeof chat !== 'undefined' && floorIndex >= chat.length)) {2141 if (isNaN(floorIndex) || floorIndex < 0 || (chatLength !== -1 && floorIndex >= chatLength)) {
2148 const maxIndex = (typeof chat !== 'undefined' ? chat.length - 1 : 'unknown');2142 const maxIndex = (chatLength !== -1 ? chatLength - 1 : 'unknown');
2149 toastr.warning(`Invalid message index: ${index}. Please enter a number between 0 and ${maxIndex}.`);2143 toastr.warning(`Invalid message index: ${index}. Please enter a number between 0 and ${maxIndex}.`);
2150 console.warn(`WARN: Invalid message index provided for /goto-floor: ${index}. Max index: ${maxIndex}`);2144 console.warn(`WARN: Invalid message index provided for /goto-floor: ${index}. Max index: ${maxIndex}`);
2151 return '';2145 return '';
2152 }2146 }
2153 2147
2148 // --- Load all messages first to ensure the target element exists ---
2149 console.log(`INFO: Attempting to load all messages before attempting to goto-floor ${index}.`);
2150 try {
2151 // Assuming showMoreMessages is available globally or within scope
2152 await showMoreMessages(Number.MAX_SAFE_INTEGER);
2153 console.log(`INFO: All messages loaded (or loading initiated).`);
2154 // Give the rendering a moment to potentially catch up after showMoreMessages2154 // Give the rendering a moment to potentially catch up after showMoreMessages
2155 // This might be necessary depending on how showMoreMessages works internally
2156 await new Promise(resolve => setTimeout(resolve, 100)); // Adjust delay if needed2155 await new Promise(resolve => setTimeout(resolve, 100)); // Adjust delay if needed
2156 } catch (error) {
2157 console.error('Error loading messages:', error);
2158 toastr.error('An error occurred while trying to load messages.');
2159 return ''; // Exit if loading fails
2160 }
2161 // --- End of loading step ---
2157 2162
2158 const messageElement = document.querySelector(`[mesid="${floorIndex}"]`);2163 const messageElement = document.querySelector(`[mesid="${floorIndex}"]`);
2159 2164
2160 if (messageElement) {2165 if (messageElement) {
2161 const headerElement = messageElement.querySelector('.mes_header') ||2166 // --- Corrected: Use the actual class from the template ---
2162 messageElement.querySelector('.mes_meta') ||2167 const headerElement = messageElement.querySelector('.ch_name');
2163 messageElement.querySelector('.mes_name_area') ||2168 const elementToScroll = headerElement || messageElement; // Fallback to the entire message div
2164 messageElement.querySelector('.mes_name');2169 const blockPosition = headerElement ? 'center' : 'start';
2165
2166 const elementToScroll = headerElement || messageElement; // Prefer header, fallback to whole message
2167 const blockPosition = headerElement ? 'center' : 'start'; // Center header, else start of message
2168 2170
2169 elementToScroll.scrollIntoView({ behavior: 'smooth', block: blockPosition });2171 elementToScroll.scrollIntoView({ behavior: 'smooth', block: blockPosition });
2170 console.log(`INFO: Scrolled ${headerElement ? 'header of' : ''} message ${floorIndex} into view (block: ${blockPosition}).`);
2171 2172
2172 // --- Highlight with smooth animation ---2173 // Highlight the message element
2173 messageElement.classList.add('highlight-scroll');2174 if (messageElement instanceof HTMLElement) {
2174 setTimeout(() => {2175 if (typeof $ !== 'undefined') { // Check if jQuery is available
2175 // Add a class to fade out, then remove the highlight class after fade2176 flashHighlight($(messageElement), 1500);
2176 messageElement.classList.add('highlight-scroll-fadeout');2177 } else {
2177 // Wait for fadeout transition to complete before removing the base class2178 console.warn('jQuery not available, cannot use flashHighlight.');
2178 // Match this duration to the transition duration in CSS2179 // Optional: Add a temporary CSS class highlight if jQuery/flashHighlight is missing
2180 messageElement.style.transition = 'background-color 0.5s ease';
2181 messageElement.style.backgroundColor = 'yellow'; // Or some highlight color
2179 setTimeout(() => {2182 setTimeout(() => {
2180 messageElement.classList.remove('highlight-scroll', 'highlight-scroll-fadeout');2183 messageElement.style.backgroundColor = ''; // Remove highlight
2181 }, 500); // Matches the 0.5s transition duration2184 }, 1500); // Match flash duration
2182 }, 1500); // Start fade out after 1.5 seconds2185 }
2183 2186
2184 } else {2187 } else {
2185 // This case is less likely now after showMoreMessages, but still possible2188 console.warn('Message element is not an HTMLElement, cannot flash highlight.');
2186 // if the element hasn't been added to the DOM yet for some reason after loading.
2187 toastr.warning(`Could not find element for message ${floorIndex} (using [mesid="${floorIndex}"]) even after attempting to load all messages. It might not be rendered yet. Try scrolling up or use /chat-render all again if issues persist.`);
2188 console.warn(`WARN: Element not found for message index ${floorIndex} using querySelector [mesid="${floorIndex}"] in /goto-floor, even after attempting to load all messages.`);
2189 const chatContainer = document.getElementById('chat');
2190 if (chatContainer) {
2191 chatContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });
2192 }2189 }
2190
2191
2192 } else {
2193 // Only warn if element is not found *after* attempting to load all messages
2194 toastr.warning(`Could not find element for message ${floorIndex} (using [mesid="${floorIndex}"]) even after attempting to load all messages. It might not be rendered yet or the index is invalid.`);
2195 console.warn(`WARN: Element not found for message index ${floorIndex} using querySelector [mesid="${floorIndex}"] in /goto-floor, even after attempting to load all messages.`);
2196 // Do NOT scroll the chat container in this case
2193 }2197 }
2194 return '';2198 return ''; // Return empty string as expected by some slash command parsers
2195 },2199 },
2196 unnamedArgumentList: [2200 unnamedArgumentList: [
2197 SlashCommandArgument.fromProps({2201 SlashCommandArgument.fromProps({
@@ -2204,8 +2208,9 @@ export function initDefaultSlashCommands() {
2204 helpString: `2208 helpString: `
2205 <div>2209 <div>
2206 Scrolls the chat view to the specified message index. Uses the <code>[mesid]</code> attribute for locating the message element. Index starts at 0.2210 Scrolls the chat view to the specified message index. Uses the <code>[mesid]</code> attribute for locating the message element. Index starts at 0.
2207 It attempts to center the character's name/header area within the message block. Highlights the message using the theme's 'matchedText' color with a smooth animation.2211 It attempts to center the character's name/header area within the message block by targeting the <code>.ch_name</code> element. Highlights the message using a flash animation.
2208 Automatically attempts to load all messages before scrolling to improve success rate, addressing issues with lazy loading.2212 Automatically attempts to load all messages before scrolling to improve success rate, addressing issues with lazy loading.
2213 A warning is displayed if the message element cannot be located even after attempting to load all messages.
2209 </div>2214 </div>
2210 <div>2215 <div>
2211 <strong>Example:</strong> <pre><code>/goto-floor 10</code></pre> Scrolls to the 11th message (mesid=10).2216 <strong>Example:</strong> <pre><code>/goto-floor 10</code></pre> Scrolls to the 11th message (mesid=10).
@@ -2213,55 +2218,11 @@ export function initDefaultSlashCommands() {
2213 `,2218 `,
2214 }));2219 }));
2215 2220
2216 // --- Improved CSS for highlight ---
2217 const styleId = 'goto-floor-highlight-style';2221 const styleId = 'goto-floor-highlight-style';
2218 if (!document.getElementById(styleId)) {2222 if (document.getElementById(styleId)) {
2219 const style = document.createElement('style');2223 document.getElementById(styleId).remove();
2220 style.id = styleId;
2221 style.textContent = `
2222 /* Base state for elements that *can* be highlighted */
2223 /* Ensures transition applies smoothly in both directions */
2224 .mes, .mes_block {
2225 transition: background-color 0.5s ease-in-out, box-shadow 0.5s ease-in-out;
2226 /* Add position relative if not already present, needed for potential pseudo-elements */
2227 position: relative;
2228 }
2229
2230 /* --- Highlighting Style --- */
2231 .mes.highlight-scroll,
2232 .mes_block.highlight-scroll {
2233 /* Use theme color for shadow, fallback to gold */
2234 box-shadow: 0 0 10px var(--ac-style-color-matchedText, #FFD700) !important;
2235 z-index: 5; /* Ensure highlight is visually prominent */
2236 }2224 }
22372225
2238 /* Modern browsers: Use color-mix for transparent background */
2239 @supports (background-color: color-mix(in srgb, white 50%, black)) {
2240 .mes.highlight-scroll,
2241 .mes_block.highlight-scroll {
2242 /* Mix theme color with transparent for background */
2243 background-color: color-mix(in srgb, var(--ac-style-color-matchedText, #FFD700) 35%, transparent) !important;
2244 }
2245 }
2246
2247 /* Fallback for older browsers: Use a fixed semi-transparent background */
2248 @supports not (background-color: color-mix(in srgb, white 50%, black)) {
2249 .mes.highlight-scroll,
2250 .mes_block.highlight-scroll {
2251 background-color: rgba(255, 215, 0, 0.35) !important; /* Fallback semi-transparent gold */
2252 }
2253 }
2254
2255 /* --- Fade-out Control --- */
2256 /* When fading out, explicitly transition back to transparent/none */
2257 .mes.highlight-scroll-fadeout,
2258 .mes_block.highlight-scroll-fadeout {
2259 background-color: transparent !important;
2260 box-shadow: none !important;
2261 }
2262 `;
2263 document.head.appendChild(style);
2264 }
2265 registerVariableCommands();2226 registerVariableCommands();
2266}2227}
22672228