Refactor event handling functions

This commit is contained in:
newtextdoc1111
2025-05-03 10:35:13 +09:00
parent 03df245362
commit 2cd74b1750
5 changed files with 183 additions and 188 deletions
+73 -112
View File
@@ -8,10 +8,6 @@ import {
escapeParentheses,
unescapeParentheses
} from './helper.js';
import {
SimilarTagsEventHandler,
} from './similar-tags.js';
import { registerEventHandlers } from './event-handler.js';
// --- Autocomplete UI Class ---
@@ -470,7 +466,6 @@ class AutocompleteUI {
// --- Autocomplete Logic ---
const autocompleteUI = new AutocompleteUI();
const similarTagsEventHandler = new SimilarTagsEventHandler(); // Pass the autocomplete UI instance
/**
* Finds tag completion candidates based on the input query.
@@ -642,116 +637,82 @@ function insertTag(inputElement, tagToInsert) {
inputElement.dispatchEvent(new Event('input', { bubbles: true }));
}
// --- Event Handlers ---
function handleInput(event) {
if (!settingValues.enabled || !autocompleteUI) return;
const ESCAPE_SEQUENCE = ["#", "/"];
const textareaElement = event.target;
const partialTag = getCurrentPartialTag(textareaElement);
if (partialTag.length > 0 && !ESCAPE_SEQUENCE.some(seq => partialTag.startsWith(seq))) {
const candidates = findCompletionCandidates(partialTag);
autocompleteUI.show(textareaElement, candidates);
} else {
autocompleteUI.hide();
}
similarTagsEventHandler.handleInput(event);
}
function handleFocus(event) {
if (!settingValues.enabled) return;
// Potentially show suggestions immediately on focus?
// For now, only show on input
if (!autocompleteUI) {
autocompleteUI = new AutocompleteUI();
}
// Maybe check if there's already text and show suggestions?
// handleInput(event); // Trigger check immediately
similarTagsEventHandler.handleFocus(event);
}
function handleBlur(event) {
// Need a slight delay because clicking the autocomplete list causes blur
setTimeout(() => {
if (autocompleteUI && !autocompleteUI.element.contains(document.activeElement)) {
export class AutocompleteEventHandler {
handleInput(event) {
if (!settingValues.enabled || !autocompleteUI) return;
const ESCAPE_SEQUENCE = ["#", "/"]; // prevent autocomplete for these sequences
const textareaElement = event.target;
const partialTag = getCurrentPartialTag(textareaElement);
if (partialTag.length > 0 && !ESCAPE_SEQUENCE.some(seq => partialTag.startsWith(seq))) {
const candidates = findCompletionCandidates(partialTag);
autocompleteUI.show(textareaElement, candidates);
} else {
autocompleteUI.hide();
}
similarTagsEventHandler.handleBlur(event);
}, 150);
}
function handleKeyDown(event) {
if (!settingValues.enabled) return;
const textareaElement = event.target;
// Handle autocomplete navigation
if (autocompleteUI && autocompleteUI.isVisible()) {
switch (event.key) {
case 'ArrowDown':
event.preventDefault();
autocompleteUI.navigate(1);
break;
case 'ArrowUp':
event.preventDefault();
autocompleteUI.navigate(-1);
break;
case 'Enter':
case 'Tab':
if (autocompleteUI.getSelectedTag() !== null) {
event.preventDefault();
insertTag(textareaElement, autocompleteUI.getSelectedTag());
} else {
// Allow default Tab/Enter if no item is selected
autocompleteUI.hide();
}
break;
case 'Escape':
event.preventDefault();
}
handleFocus(event) {
if (!settingValues.enabled) return;
// Potentially show suggestions immediately on focus?
// For now, only show on input
if (!autocompleteUI) {
autocompleteUI = new AutocompleteUI();
}
// Maybe check if there's already text and show suggestions?
// handleInput(event); // Trigger check immediately
}
handleBlur(event) {
// Need a slight delay because clicking the autocomplete list causes blur
setTimeout(() => {
if (autocompleteUI && !autocompleteUI.element.contains(document.activeElement)) {
autocompleteUI.hide();
break;
}
}, 150);
}
handleKeyDown(event) {
if (!settingValues.enabled) return;
const textareaElement = event.target;
// Handle autocomplete navigation
if (autocompleteUI && autocompleteUI.isVisible()) {
switch (event.key) {
case 'ArrowDown':
event.preventDefault();
autocompleteUI.navigate(1);
break;
case 'ArrowUp':
event.preventDefault();
autocompleteUI.navigate(-1);
break;
case 'Enter':
case 'Tab':
if (autocompleteUI.getSelectedTag() !== null) {
event.preventDefault();
insertTag(textareaElement, autocompleteUI.getSelectedTag());
} else {
// Allow default Tab/Enter if no item is selected
autocompleteUI.hide();
}
break;
case 'Escape':
event.preventDefault();
autocompleteUI.hide();
break;
}
}
}
similarTagsEventHandler.handleKeyDown(event);
}
// New event handler for mousemove to show similar tags on hover
function handleMouseMove(event) {
similarTagsEventHandler.handleMouseMove(event);
}
// New event handler for click to show similar tags
function handleClick(event) {
if (!settingValues.enabled || !settingValues.enableSimilarTags ||
settingValues.similarTagsDisplayMode !== 'click') return;
similarTagsEventHandler.handleClick(event);
}
// --- End Data Loading Functions ---
// --- Initialization ---
export async function initializeAutocomplete(rootPath) {
try {
await loadAllData(rootPath);
registerEventHandlers({
handleInput: handleInput,
handleFocus: handleFocus,
handleBlur: handleBlur,
handleKeyDown: handleKeyDown,
handleMouseMove: handleMouseMove,
handleClick: handleClick
});
console.log("[Autocomplete-Plus] Autocomplete and similar tags features initialized.");
} catch (e) {
console.error("[Autocomplete-Plus] Error during API data loading:", e);
// New event handler for mousemove to show similar tags on hover
handleMouseMove(event) {
}
}
// New event handler for click to show similar tags
handleClick(event) {
if (!settingValues.enabled || !settingValues.enableSimilarTags ||
settingValues.similarTagsDisplayMode !== 'click') return;
}
}
-63
View File
@@ -1,63 +0,0 @@
/**
* Registers event handlers for dynamically added and existing textareas in the DOM.
* This function uses a MutationObserver to monitor changes in the DOM and attaches
* event listeners to elements matching specified selectors.
*
* @param {Object} eventHandlers - An object containing the event handler functions.
* @param {Function} eventHandlers.handleInput - Function to handle the 'input' event.
* @param {Function} eventHandlers.handleFocus - Function to handle the 'focus' event.
* @param {Function} eventHandlers.handleBlur - Function to handle the 'blur' event.
* @param {Function} eventHandlers.handleKeyDown - Function to handle the 'keydown' event.
* @param {Function} eventHandlers.handleMouseMove - Function to handle the 'mousemove' event.
* @param {Function} eventHandlers.handleClick - Function to handle the 'click' event.
*/
export function registerEventHandlers(eventHandlers) {
// Find relevant textareas (e.g., prompt inputs)
// This selector might need adjustment based on ComfyUI's structure
const targetSelectors = [
'.comfy-multiline-input',
// Add other selectors if needed
];
// Use MutationObserver to detect dynamically added textareas
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE) {
targetSelectors.forEach(selector => {
// Check if the added node itself matches or contains matching elements
if (node.matches(selector)) {
attachListeners(node);
} else {
node.querySelectorAll(selector).forEach(attachListeners);
}
});
}
});
});
});
// Function to attach listeners
function attachListeners(element) {
if (element.dataset.autocompleteAttached) return; // Prevent double attachment
element.addEventListener('input', eventHandlers.handleInput);
element.addEventListener('focus', eventHandlers.handleFocus);
element.addEventListener('blur', eventHandlers.handleBlur);
element.addEventListener('keydown', eventHandlers.handleKeyDown);
// Add new event listeners for similar tags feature
element.addEventListener('mousemove', eventHandlers.handleMouseMove);
element.addEventListener('click', eventHandlers.handleClick);
element.dataset.autocompleteAttached = 'true';
}
// Initial scan for existing elements
targetSelectors.forEach(selector => {
document.querySelectorAll(selector).forEach(attachListeners);
});
// Start observing the document body for changes
observer.observe(document.body, { childList: true, subtree: true });
}
+14 -1
View File
@@ -93,4 +93,17 @@ export function unescapeParentheses(str) {
if (!str) return str;
return str.replace(/\\\(/g, '(').replace(/\\\)/g, ')');
}
// --- End String Helper Functions ---
// --- End String Helper Functions ---
// Function to load a CSS file
export function loadCSS(href) {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = href;
// Ensure the CSS is loaded before other scripts might rely on its styles
// by adding it to the head.
document.head.appendChild(link);
console.debug(`Loaded CSS: ${href}`); // Optional: Log loading
}
+92 -12
View File
@@ -1,17 +1,95 @@
import { app } from "/scripts/app.js";
import { initializeAutocomplete } from './autocomplete.js';
import { settingValues } from "./settings.js";
import { loadCSS } from "./helper.js";
import { loadAllData } from "./data.js";
import { AutocompleteEventHandler } from "./autocomplete.js";
import { SimilarTagsEventHandler } from "./similar-tags.js";
// Function to load a CSS file
function loadCSS(href) {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = href;
// Ensure the CSS is loaded before other scripts might rely on its styles
// by adding it to the head.
document.head.appendChild(link);
console.debug(`Loaded CSS: ${href}`); // Optional: Log loading
function initializeEventHandlers() {
// Singletons for event handlers
const autocompleteEventHandler = new AutocompleteEventHandler();
const similarTagsEventHandler = new SimilarTagsEventHandler();
// Find relevant textareas (e.g., prompt inputs)
// This selector might need adjustment based on ComfyUI's structure
const targetSelectors = [
'.comfy-multiline-input',
// Add other selectors if needed
];
// Use MutationObserver to detect dynamically added textareas
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE) {
targetSelectors.forEach(selector => {
// Check if the added node itself matches or contains matching elements
if (node.matches(selector)) {
attachListeners(node);
} else {
node.querySelectorAll(selector).forEach(attachListeners);
}
});
}
});
});
});
// Function to attach listeners
function attachListeners(element) {
if (element.dataset.autocompleteAttached) return; // Prevent double attachment
element.addEventListener('input', handleInput);
element.addEventListener('focus', handleFocus);
element.addEventListener('blur', handleBlur);
element.addEventListener('keydown', handleKeyDown);
// Add new event listeners for similar tags feature
element.addEventListener('mousemove', handleMouseMove);
element.addEventListener('click', handleClick);
element.dataset.autocompleteAttached = 'true';
}
// Initial scan for existing elements
targetSelectors.forEach(selector => {
document.querySelectorAll(selector).forEach(attachListeners);
});
// Start observing the document body for changes
observer.observe(document.body, { childList: true, subtree: true });
function handleInput(event) {
autocompleteEventHandler.handleInput(event);
similarTagsEventHandler.handleInput(event);
}
function handleFocus(event) {
autocompleteEventHandler.handleFocus(event);
similarTagsEventHandler.handleFocus(event);
}
function handleBlur(event) {
autocompleteEventHandler.handleBlur(event);
similarTagsEventHandler.handleBlur(event);
}
function handleKeyDown(event) {
autocompleteEventHandler.handleKeyDown(event);
similarTagsEventHandler.handleKeyDown(event);
}
// New event handler for mousemove to show similar tags on hover
function handleMouseMove(event) {
autocompleteEventHandler.handleMouseMove(event);
similarTagsEventHandler.handleMouseMove(event);
}
// New event handler for click to show similar tags
function handleClick(event) {
autocompleteEventHandler.handleClick(event);
similarTagsEventHandler.handleClick(event);
}
}
const id = "AutocompletePlus";
@@ -20,10 +98,12 @@ app.registerExtension({
id: id,
name: name,
setup() {
initializeEventHandlers();
let rootPath = import.meta.url.replace("js/main.js", "");
loadCSS(rootPath + "css/autocomplete-plus.css"); // Load CSS for autocomplete
initializeAutocomplete(rootPath); // Initialize after tags are loaded
loadAllData(rootPath);
},
settings: [
{
+4
View File
@@ -5,4 +5,8 @@ export const settingValues = {
enableSimilarTags: true,
maxSimilarTags: 20,
similarTagsDisplayMode: 'click', // 'hover' or 'click'
}
export function initSettings(){
}