From 70f916b8256caca847c1ce2e5e76b1dccf86e703 Mon Sep 17 00:00:00 2001 From: BlakeNeko Date: Thu, 13 Nov 2025 11:11:02 +0800 Subject: [PATCH 01/10] feat: implement prompt auto-formatter function - Splits text by lines and cleans up tags separated by commas - Removes redundant spaces and empty tags - Ensures each valid line ends with a comma and space - Preserves empty lines for readability --- web/js/utils.js | 51 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/web/js/utils.js b/web/js/utils.js index ed59fc8..7a14651 100644 --- a/web/js/utils.js +++ b/web/js/utils.js @@ -654,3 +654,54 @@ export function getScrollbarWidth() { return _cachedScrollbarWidth; } + +/** + * 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'); +} From edbea2bb43ee50ac8e29cd076db6395d4fc5f948 Mon Sep 17 00:00:00 2001 From: BlakeNeko Date: Thu, 13 Nov 2025 11:21:14 +0800 Subject: [PATCH 02/10] feat: add auto format settings --- web/js/main.js | 13 +++++++++++++ web/js/settings.js | 3 +++ 2 files changed, 16 insertions(+) diff --git a/web/js/main.js b/web/js/main.js index 9facef4..c59110f 100644 --- a/web/js/main.js +++ b/web/js/main.js @@ -416,5 +416,18 @@ app.registerExtension({ settingValues.hideAlias = newVal; } }, + + // --- Auto format settings --- + { + id: id + '.enable_auto_format', + name: 'Enable Auto Format on Blur', + description: 'Automatically format prompts when leaving the input field (add commas and spaces, remove extra spaces)', + type: 'boolean', + defaultValue: true, + category: [name, 'Autocompletion', '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..d6d587f 100644 --- a/web/js/settings.js +++ b/web/js/settings.js @@ -29,6 +29,9 @@ export const settingValues = { // Debugging settings (use internally) _hideWhenOutofFocus: true, // Hide UI when the input is out of focus _logprocessingTime: false, // Log processing time for debugging + + // Auto format settings + enableAutoFormat: true, } /** From 7f23b028128b0f896686862a7328722b78b5925d Mon Sep 17 00:00:00 2001 From: BlakeNeko Date: Thu, 13 Nov 2025 11:24:57 +0800 Subject: [PATCH 03/10] feat: add a helper function and trigger it on textarea blur --- web/js/main.js | 5 +++++ web/js/utils.js | 13 +++++++++++++ 2 files changed, 18 insertions(+) diff --git a/web/js/main.js b/web/js/main.js index c59110f..ac65c5d 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 { formatTextareaOnBlur } from './utils.js'; // --- Constants --- const id = "AutocompletePlus"; @@ -105,6 +106,10 @@ function initializeEventHandlers() { function handleBlur(event) { autocompleteEventHandler.handleBlur(event); relatedTagsEventHandler.handleBlur(event); + + if (settingValues.enableAutoFormat && event.target.tagName === 'TEXTAREA') { + formatTextareaOnBlur(event.target); + } } function handleKeyDown(event) { diff --git a/web/js/utils.js b/web/js/utils.js index 7a14651..8c0d941 100644 --- a/web/js/utils.js +++ b/web/js/utils.js @@ -705,3 +705,16 @@ export function formatPromptText(text) { // 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) { + textarea.setRangeText(formattedText, 0, originalText.length, 'end'); + } +} \ No newline at end of file From 80d254c6b45c3273107850fc423089aa8b80d125 Mon Sep 17 00:00:00 2001 From: BlakeNeko Date: Sat, 15 Nov 2025 22:11:46 +0800 Subject: [PATCH 04/10] refactor: Move formatPromptText and formatTextareaOnBlur functions from utils.js to auto-formatter.js --- web/js/auto-formatter.js | 63 +++++++++++++++++++++++++++++++++++++++ web/js/main.js | 2 +- web/js/utils.js | 64 ---------------------------------------- 3 files changed, 64 insertions(+), 65 deletions(-) create mode 100644 web/js/auto-formatter.js diff --git a/web/js/auto-formatter.js b/web/js/auto-formatter.js new file mode 100644 index 0000000..839cc15 --- /dev/null +++ b/web/js/auto-formatter.js @@ -0,0 +1,63 @@ +/** + * 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) { + textarea.setRangeText(formattedText, 0, originalText.length, 'end'); + } +} diff --git a/web/js/main.js b/web/js/main.js index ac65c5d..0dc6530 100644 --- a/web/js/main.js +++ b/web/js/main.js @@ -6,7 +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 { formatTextareaOnBlur } from './utils.js'; +import { formatTextareaOnBlur } from "./auto-formatter.js"; // --- Constants --- const id = "AutocompletePlus"; diff --git a/web/js/utils.js b/web/js/utils.js index 8c0d941..ed59fc8 100644 --- a/web/js/utils.js +++ b/web/js/utils.js @@ -654,67 +654,3 @@ export function getScrollbarWidth() { return _cachedScrollbarWidth; } - -/** - * 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) { - textarea.setRangeText(formattedText, 0, originalText.length, 'end'); - } -} \ No newline at end of file From ee8b2651731144cbdefcb4136bfd07abb28ccc59 Mon Sep 17 00:00:00 2001 From: BlakeNeko Date: Sat, 15 Nov 2025 22:13:59 +0800 Subject: [PATCH 05/10] feat: Preserving the previous cursor position --- web/js/auto-formatter.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/web/js/auto-formatter.js b/web/js/auto-formatter.js index 839cc15..6554ed0 100644 --- a/web/js/auto-formatter.js +++ b/web/js/auto-formatter.js @@ -58,6 +58,11 @@ export function formatTextareaOnBlur(textarea) { const formattedText = formatPromptText(originalText); if (originalText !== formattedText) { - textarea.setRangeText(formattedText, 0, originalText.length, 'end'); + const cursorPos = textarea.selectionStart; + textarea.value = formattedText; + + // Try to preserve cursor position + const newCursorPos = Math.min(cursorPos, formattedText.length); + textarea.setSelectionRange(newCursorPos, newCursorPos); } } From 7a279d71c3d19c54e8a8deb049e9ec46228b90fd Mon Sep 17 00:00:00 2001 From: BlakeNeko Date: Sat, 15 Nov 2025 22:15:59 +0800 Subject: [PATCH 06/10] refactor: Moving the enableAutoFormat setting --- web/js/settings.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/js/settings.js b/web/js/settings.js index d6d587f..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 @@ -29,9 +32,6 @@ export const settingValues = { // Debugging settings (use internally) _hideWhenOutofFocus: true, // Hide UI when the input is out of focus _logprocessingTime: false, // Log processing time for debugging - - // Auto format settings - enableAutoFormat: true, } /** From e622148b274e3a8f280af62a3e9a9428cc54c099 Mon Sep 17 00:00:00 2001 From: BlakeNeko Date: Sat, 15 Nov 2025 22:25:48 +0800 Subject: [PATCH 07/10] fix: Update settings to support internationalization properly --- web/js/main.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/js/main.js b/web/js/main.js index 0dc6530..45cf085 100644 --- a/web/js/main.js +++ b/web/js/main.js @@ -424,12 +424,12 @@ app.registerExtension({ // --- Auto format settings --- { - id: id + '.enable_auto_format', + id: id + '.AutoFormatter.EnableAutoFormat', name: 'Enable Auto Format on Blur', description: 'Automatically format prompts when leaving the input field (add commas and spaces, remove extra spaces)', type: 'boolean', defaultValue: true, - category: [name, 'Autocompletion', 'Enable Auto Format'], + category: [name, 'AutoFormatter', 'Enable Auto Format'], onChange: (newVal, oldVal) => { settingValues.enableAutoFormat = newVal; }, From 4b64fae70df6a917b7272be9a2c265fc7fabf7d1 Mon Sep 17 00:00:00 2001 From: BlakeNeko Date: Sat, 15 Nov 2025 23:17:28 +0800 Subject: [PATCH 08/10] fix: Simplify auto format setting name and description --- web/js/main.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/js/main.js b/web/js/main.js index 45cf085..65f3586 100644 --- a/web/js/main.js +++ b/web/js/main.js @@ -425,8 +425,8 @@ app.registerExtension({ // --- Auto format settings --- { id: id + '.AutoFormatter.EnableAutoFormat', - name: 'Enable Auto Format on Blur', - description: 'Automatically format prompts when leaving the input field (add commas and spaces, remove extra spaces)', + name: 'Enable Auto Format', + description: 'Automatically format prompts when leaving the input field.', type: 'boolean', defaultValue: true, category: [name, 'AutoFormatter', 'Enable Auto Format'], From e8b0e44cf4a03043ebdc401d944002015df8a70b Mon Sep 17 00:00:00 2001 From: BlakeNeko Date: Sat, 15 Nov 2025 23:31:59 +0800 Subject: [PATCH 09/10] fix: Remove an invalid setting option --- web/js/main.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/web/js/main.js b/web/js/main.js index 45cf085..a7d0ec3 100644 --- a/web/js/main.js +++ b/web/js/main.js @@ -425,8 +425,7 @@ app.registerExtension({ // --- Auto format settings --- { id: id + '.AutoFormatter.EnableAutoFormat', - name: 'Enable Auto Format on Blur', - description: 'Automatically format prompts when leaving the input field (add commas and spaces, remove extra spaces)', + name: 'Enable Auto Format', type: 'boolean', defaultValue: true, category: [name, 'AutoFormatter', 'Enable Auto Format'], From 56f506110de86f9e9528c1bbf2d3d2c308ab7f83 Mon Sep 17 00:00:00 2001 From: BlakeNeko Date: Tue, 18 Nov 2025 11:15:02 +0800 Subject: [PATCH 10/10] refactor: use AutoFormatterEventHandler to maintain architectural consistency, format code indent --- web/js/auto-formatter.js | 122 ++++++++++++++++++++++++--------------- web/js/main.js | 8 +-- 2 files changed, 79 insertions(+), 51 deletions(-) diff --git a/web/js/auto-formatter.js b/web/js/auto-formatter.js index 6554ed0..3229501 100644 --- a/web/js/auto-formatter.js +++ b/web/js/auto-formatter.js @@ -1,3 +1,33 @@ +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. @@ -5,48 +35,48 @@ * @returns {string} - The formatted text. */ export function formatPromptText(text) { - if (!text || text.trim().length === 0) return text; + if (!text || text.trim().length === 0) return text; - // Split text into individual lines for processing - const lines = text.split('\n'); - const formattedLines = []; + // Split text into individual lines for processing + const lines = text.split('\n'); + const formattedLines = []; - for (const line of lines) { - const trimmedLine = line.trim(); + for (const line of lines) { + const trimmedLine = line.trim(); - // Keep empty lines unchanged - if (trimmedLine.length === 0) { - formattedLines.push(''); - continue; + // 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); } - // 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'); + // Rejoin all lines with newline characters + return formattedLines.join('\n'); } /** @@ -54,15 +84,15 @@ export function formatPromptText(text) { * @param {HTMLTextAreaElement} textarea - The target textarea element. */ export function formatTextareaOnBlur(textarea) { - const originalText = textarea.value; - const formattedText = formatPromptText(originalText); + const originalText = textarea.value; + const formattedText = formatPromptText(originalText); - if (originalText !== formattedText) { - const cursorPos = textarea.selectionStart; - textarea.value = formattedText; + 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); - } + // 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 a7d0ec3..e6e3836 100644 --- a/web/js/main.js +++ b/web/js/main.js @@ -6,7 +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 { formatTextareaOnBlur } from "./auto-formatter.js"; +import { AutoFormatterEventHandler } from "./auto-formatter.js"; // --- Constants --- const id = "AutocompletePlus"; @@ -19,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 @@ -106,10 +107,7 @@ function initializeEventHandlers() { function handleBlur(event) { autocompleteEventHandler.handleBlur(event); relatedTagsEventHandler.handleBlur(event); - - if (settingValues.enableAutoFormat && event.target.tagName === 'TEXTAREA') { - formatTextareaOnBlur(event.target); - } + autoFormatterEventHandler.handleBlur(event); } function handleKeyDown(event) {