import { settingValues } from './settings.js'; import { NodeInfo } from './node-info.js'; /** * Determines if the text content should be auto-formatted. * * Format conditions: * 1. Skip formatting if node is in blocklist * 2. If text is empty after trimming, format only if trimSurroundingSpaces is enabled, otherwise don't format * 3. Skip formatting if text contains only numbers or single letters (separated by commas) * 4. Format if text contains "word + comma" pattern * 5. Format if trimSurroundingSpaces is enabled and there are surrounding spaces or line breaks * 6. Otherwise, don't format * * @param {NodeInfo} nodeInfo - The node information. * @returns {boolean} - True if the text should be formatted, false otherwise. */ function shouldAutoFormat(text, nodeInfo) { if (!text) return false; // 1. Check if the node name is in the blocklist const blocklist = [ ["Power Puter (rgthree)", "code"], ["LoraLoaderBlockWeight //Inspire", "block_vector"] ]; const isBlocklisted = blocklist.some(([type, input]) => type === nodeInfo.nodeType && input === nodeInfo.inputName ); if (isBlocklisted) { // console.debug(`[Autocomplete-Plus] auto-formatter on blur => nodeType: ${nodeInfo.nodeType}, inputName: ${nodeInfo.inputName} => blocklisted`); return false; } else { // console.debug(`[Autocomplete-Plus] auto-formatter on blur => nodeType: ${nodeInfo.nodeType}, inputName: ${nodeInfo.inputName}`); } const trimmedText = text.trim(); // 2. Check if the text is completely empty after trimming if (trimmedText.length === 0) { return settingValues.trimSurroundingSpaces; } // 3. Check if the text is purely numeric data or single-letter placeholders with commas // (e.g., "0,0,0,1,1,1" or "0.5, -1.2, 0.8" or "A,B,R" for LoRA Block Weight) const elements = trimmedText.split(',').map(el => el.trim()); const isSingleLetterOrNumeric = elements.every(el => { if (/^[A-Za-z]$/.test(el)) return true; if (/^-?\d+(\.\d+)?$/.test(el)) return true; return false; }); if (isSingleLetterOrNumeric && elements.length > 0) { return false; // Don't format numeric data or single-letter template patterns } // 4. If text contains "word + comma" pattern, format it const wordCommaPattern = /\w+\s*,/g; if (trimmedText.match(wordCommaPattern)) { return true; } // 5. If trimSurroundingSpaces is enabled and there are surrounding spaces, format to trim them if (settingValues.trimSurroundingSpaces && text !== trimmedText) { return true; } return false; // Otherwise, don't format } /** * Format the prompt text: add a comma and space after each tag, and remove extra spaces. * Preserve special syntax such as weights (tag:1.2), parentheses, and wildcards. * @param {string} text - The original text. * @returns {string} - The formatted text. */ export function formatPromptText(text) { // Handle null or undefined input if (text == null) return text; // If the text is empty or contains only spaces... if (text.trim().length === 0) { if (settingValues.trimSurroundingSpaces) { // ...And trimSurroundingSpaces is enabled, return an empty string return ''; } else { // ...Otherwise, return the original text return text; } } let processedText = text; if (settingValues.trimSurroundingSpaces) { processedText = text.trim(); } // Split text into individual lines for processing const lines = processedText.split('\n'); const formattedLines = []; for (const line of lines) { const trimmedLine = line.trim(); // Keep empty lines unchanged if (trimmedLine.length === 0) { formattedLines.push(''); continue; } // Split the line by commas into raw tag segments const rawTags = trimmedLine.split(','); const cleanedTags = []; for (const tag of rawTags) { // Trim spaces around each tag const trimmedTag = tag.trim(); // Keep only non-empty tags if (trimmedTag.length > 0) { cleanedTags.push(trimmedTag); } } // Rejoin cleaned tags with ", " let formattedLine = cleanedTags.join(', '); // Add a trailing comma and space if the setting is enabled and line contains valid tags if (formattedLine.length > 0 && settingValues.useTrailingComma) { formattedLine += ', '; } formattedLines.push(formattedLine); } // Rejoin all lines with newline characters return formattedLines.join('\n'); } /** * Format the content of a textarea when it loses focus. * @param {HTMLTextAreaElement} textarea - The target textarea element. */ export function formatTextareaOnBlur(textarea) { const originalText = textarea.value; const formattedText = formatPromptText(originalText); if (originalText !== formattedText) { const cursorPos = textarea.selectionStart; textarea.value = formattedText; // Try to preserve cursor position const newCursorPos = Math.min(cursorPos, formattedText.length); textarea.setSelectionRange(newCursorPos, newCursorPos); } } /** * Event handler for auto-formatting functionality */ export class AutoFormatterEventHandler { constructor() { } /** * Handle blur event to trigger auto-formatting * @param {Event} event - The blur event */ handleBlur(event, nodeInfo) { if ( settingValues.enableAutoFormat && settingValues.autoFormatTrigger === 'auto' && event.target.tagName === 'TEXTAREA' ) { const textarea = event.target; const text = textarea.value; // Check if the content should be auto-formatted if (shouldAutoFormat(text, nodeInfo)) { formatTextareaOnBlur(textarea); } } } // Placeholder methods to maintain consistency with other event handlers handleInput(event) { } handleFocus(event) { } handleKeyDown(event) { } handleKeyUp(event) { } handleMouseMove(event) { } handleClick(event) { } /** * Format textarea content via manual trigger (e.g., keyboard shortcut) * @param {HTMLTextAreaElement} textarea - The textarea element to format * @param {NodeInfo} nodeInfo - The node information * @returns {boolean} - True if formatting was performed, false otherwise */ applyFormatTextarea(textarea, nodeInfo) { if (!textarea || textarea.tagName !== 'TEXTAREA') { return false; } if (!settingValues.enableAutoFormat) { return false; } const text = textarea.value; if (shouldAutoFormat(text, nodeInfo)) { formatTextareaOnBlur(textarea); return true; } return false; } } // Export functions for testing const isTestEnvironment = typeof process !== 'undefined' && process.env.NODE_ENV === 'test'; export const __test__ = isTestEnvironment ? { shouldAutoFormat } : undefined;