diff --git a/web/js/auto-formatter.js b/web/js/auto-formatter.js new file mode 100644 index 0000000..3229501 --- /dev/null +++ b/web/js/auto-formatter.js @@ -0,0 +1,98 @@ +import { settingValues } from './settings.js'; + +/** + * 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) { + if ( + settingValues.enableAutoFormat && + event.target.tagName === 'TEXTAREA' + ) { + formatTextareaOnBlur(event.target); + } + } + + // Placeholder methods to maintain consistency with other event handlers + handleInput(event) {} + handleFocus(event) {} + handleKeyDown(event) {} + handleKeyUp(event) {} + handleMouseMove(event) {} + handleClick(event) {} +} + +/** + * 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) { + if (!text || text.trim().length === 0) return text; + + // Split text into individual lines for processing + const lines = text.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 line contains valid tags + if (formattedLine.length > 0) { + 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); + } +} diff --git a/web/js/main.js b/web/js/main.js index 9facef4..e6e3836 100644 --- a/web/js/main.js +++ b/web/js/main.js @@ -6,6 +6,7 @@ import { loadCSS } from "./utils.js"; import { TagSource, loadDataAsync } from "./data.js"; import { AutocompleteEventHandler } from "./autocomplete.js"; import { RelatedTagsEventHandler } from "./related-tags.js"; +import { AutoFormatterEventHandler } from "./auto-formatter.js"; // --- Constants --- const id = "AutocompletePlus"; @@ -18,6 +19,7 @@ const name = "Autocomplete Plus"; function initializeEventHandlers() { const autocompleteEventHandler = new AutocompleteEventHandler(); const relatedTagsEventHandler = new RelatedTagsEventHandler(); + const autoFormatterEventHandler = new AutoFormatterEventHandler(); const attachedElements = new WeakSet(); // Keep track of elements that have listeners attached // Function to attach listeners @@ -105,6 +107,7 @@ function initializeEventHandlers() { function handleBlur(event) { autocompleteEventHandler.handleBlur(event); relatedTagsEventHandler.handleBlur(event); + autoFormatterEventHandler.handleBlur(event); } function handleKeyDown(event) { @@ -416,5 +419,17 @@ app.registerExtension({ settingValues.hideAlias = newVal; } }, + + // --- Auto format settings --- + { + id: id + '.AutoFormatter.EnableAutoFormat', + name: 'Enable Auto Format', + type: 'boolean', + defaultValue: true, + category: [name, 'AutoFormatter', 'Enable Auto Format'], + onChange: (newVal, oldVal) => { + settingValues.enableAutoFormat = newVal; + }, + }, ] }); \ No newline at end of file diff --git a/web/js/settings.js b/web/js/settings.js index 1e12424..177b620 100644 --- a/web/js/settings.js +++ b/web/js/settings.js @@ -21,6 +21,9 @@ export const settingValues = { // Display settings hideAlias: false, // Hide alias in the autocomplete and related tags display + // Auto format settings + enableAutoFormat: true, + // Internal logic settings _useFallbackAttachmentForEventListener: false, // Fallback to attach event listener when somthing goes wrong