From f68c0c7dc2003ff4b3712e654f7802d5ee36a83a Mon Sep 17 00:00:00 2001 From: newtextdoc1111 Date: Thu, 20 Nov 2025 22:00:15 +0900 Subject: [PATCH 1/7] feat: Implement auto-formatting block logic --- web/js/auto-formatter.js | 65 ++++++++++++++++++++++++++++++++-- web/js/main.js | 75 ++++++++++++++++++++++++++++++++++------ web/js/node-info.js | 14 ++++++++ 3 files changed, 142 insertions(+), 12 deletions(-) create mode 100644 web/js/node-info.js diff --git a/web/js/auto-formatter.js b/web/js/auto-formatter.js index f48b3d1..afc6bbe 100644 --- a/web/js/auto-formatter.js +++ b/web/js/auto-formatter.js @@ -1,4 +1,59 @@ 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. Skip formatting if text contains only numbers, hyphens, commas, dots, and spaces + * 3. Format if text contains "word + comma" pattern at least twice + * 4. 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 || text.trim().length === 0) 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 purely numeric data with hyphens and commas + // (e.g., "0,0,0,1,1,1" or "0.5, -1.2, 0.8" for LoRA Block Weight) + const numericPattern = /^[\d.,\s-]+$/; + if (numericPattern.test(trimmedText)) { + return false; // Don't format numeric data + } + + // 3. Check if the text contains the pattern "word + comma" + const wordCommaPattern = /\w+\s*,/g; + const matches = trimmedText.match(wordCommaPattern); + + if (matches == null) { + return false; + } + + return true; // Text should be formatted +} /** * Format the prompt text: add a comma and space after each tag, and remove extra spaces. @@ -79,12 +134,18 @@ export class AutoFormatterEventHandler { * Handle blur event to trigger auto-formatting * @param {Event} event - The blur event */ - handleBlur(event) { + handleBlur(event, nodeInfo) { if ( settingValues.enableAutoFormat && event.target.tagName === 'TEXTAREA' ) { - formatTextareaOnBlur(event.target); + const textarea = event.target; + const text = textarea.value; + + // Check if the content should be auto-formatted + if (shouldAutoFormat(text, nodeInfo)) { + formatTextareaOnBlur(textarea); + } } } diff --git a/web/js/main.js b/web/js/main.js index e6e3836..29fcd1c 100644 --- a/web/js/main.js +++ b/web/js/main.js @@ -7,6 +7,7 @@ import { TagSource, loadDataAsync } from "./data.js"; import { AutocompleteEventHandler } from "./autocomplete.js"; import { RelatedTagsEventHandler } from "./related-tags.js"; import { AutoFormatterEventHandler } from "./auto-formatter.js"; +import { NodeInfo } from "./node-info.js"; // --- Constants --- const id = "AutocompletePlus"; @@ -20,11 +21,11 @@ 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 + const attachedElementNodeInfoMap = new WeakMap(); // Map to track attached elements and their node info // Function to attach listeners - function attachListeners(element) { - if (attachedElements.has(element)) return; // Prevent double attachment + function attachListeners(element, nodeInfo) { + if (attachedElementNodeInfoMap.has(element)) return; // Prevent double attachment element.addEventListener('input', handleInput); element.addEventListener('focus', handleFocus); @@ -37,7 +38,7 @@ function initializeEventHandlers() { element.addEventListener('mousemove', handleMouseMove); element.addEventListener('click', handleClick); - attachedElements.add(element); // Mark as attached + attachedElementNodeInfoMap.set(element, nodeInfo); // Mark as attached and store node info } // Attempt Widget Override as the primary method @@ -50,12 +51,14 @@ function initializeEventHandlers() { // Check if the widget has an inputEl and if it's a TEXTAREA // This is to ensure we are targeting multiline text inputs, related to '.comfy-multiline-input' - if (result && result.widget && result.widget.inputEl && result.widget.inputEl.tagName === 'TEXTAREA') { + if (result && result.widget + && result.widget.inputEl && result.widget.inputEl.tagName === 'TEXTAREA' && !result.widget.inputEl.readOnly) { const widgetConfig = inputData && inputData[1] ? inputData[1] : {}; // Future: Add checks for Autocomplete Plus specific configurations if needed // e.g., if (widgetConfig["AutocompletePlus.enabled"] === false) return result; - attachListeners(result.widget.inputEl); + const nodeInfo = new NodeInfo(node.comfyClass || node.constructor.name, inputName); + attachListeners(result.widget.inputEl, nodeInfo); } return result; }; @@ -75,9 +78,11 @@ function initializeEventHandlers() { targetSelectors.forEach(selector => { // Check if the added node itself matches or contains matching elements if (node.matches(selector)) { - attachListeners(node); + attachListeners(node, new NodeInfo('Fallback', 'unknown')); } else { - node.querySelectorAll(selector).forEach(attachListeners); + node.querySelectorAll(selector).forEach(el => { + attachListeners(el, new NodeInfo('Fallback', 'unknown')); + }); } }); } @@ -87,7 +92,9 @@ function initializeEventHandlers() { // Initial scan for existing elements targetSelectors.forEach(selector => { - document.querySelectorAll(selector).forEach(attachListeners); + document.querySelectorAll(selector).forEach(el => { + attachListeners(el, new NodeInfo('Fallback', 'unknown')); + }); }); // Start observing the document body for changes @@ -95,41 +102,89 @@ function initializeEventHandlers() { } function handleInput(event) { + const nodeInfo = attachedElementNodeInfoMap.get(event.target); + if (!nodeInfo) { + console.warn('[Autocomplete-Plus] Node info not found for element in handleInput:', event.target); + return; + } + autocompleteEventHandler.handleInput(event); relatedTagsEventHandler.handleInput(event); + autoFormatterEventHandler.handleInput(event); } function handleFocus(event) { + const nodeInfo = attachedElementNodeInfoMap.get(event.target); + if (!nodeInfo) { + console.warn('[Autocomplete-Plus] Node info not found for element in handleFocus:', event.target); + return; + } + autocompleteEventHandler.handleFocus(event); relatedTagsEventHandler.handleFocus(event); + autoFormatterEventHandler.handleFocus(event); } function handleBlur(event) { + const nodeInfo = attachedElementNodeInfoMap.get(event.target); + if (!nodeInfo) { + console.warn('[Autocomplete-Plus] Node info not found for element in handleBlur:', event.target); + return; + } + autocompleteEventHandler.handleBlur(event); relatedTagsEventHandler.handleBlur(event); - autoFormatterEventHandler.handleBlur(event); + autoFormatterEventHandler.handleBlur(event, nodeInfo); } function handleKeyDown(event) { + const nodeInfo = attachedElementNodeInfoMap.get(event.target); + if (!nodeInfo) { + console.warn('[Autocomplete-Plus] Node info not found for element in handleKeyDown:', event.target); + return; + } + autocompleteEventHandler.handleKeyDown(event); relatedTagsEventHandler.handleKeyDown(event); + autoFormatterEventHandler.handleKeyDown(event); } function handleKeyUp(event) { + const nodeInfo = attachedElementNodeInfoMap.get(event.target); + if (!nodeInfo) { + console.warn('[Autocomplete-Plus] Node info not found for element in handleKeyUp:', event.target); + return; + } + autocompleteEventHandler.handleKeyUp(event); relatedTagsEventHandler.handleKeyUp(event); + autoFormatterEventHandler.handleKeyUp(event); } // New event handler for mousemove to show related tags on hover function handleMouseMove(event) { + const nodeInfo = attachedElementNodeInfoMap.get(event.target); + if (!nodeInfo) { + console.warn('[Autocomplete-Plus] Node info not found for element in handleMouseMove:', event.target); + return; + } + autocompleteEventHandler.handleMouseMove(event); relatedTagsEventHandler.handleMouseMove(event); + autoFormatterEventHandler.handleMouseMove(event); } // New event handler for click to show related tags function handleClick(event) { + const nodeInfo = attachedElementNodeInfoMap.get(event.target); + if (!nodeInfo) { + console.warn('[Autocomplete-Plus] Node info not found for element in handleClick:', event.target); + return; + } + autocompleteEventHandler.handleClick(event); relatedTagsEventHandler.handleClick(event); + autoFormatterEventHandler.handleClick(event); } } diff --git a/web/js/node-info.js b/web/js/node-info.js new file mode 100644 index 0000000..f112d38 --- /dev/null +++ b/web/js/node-info.js @@ -0,0 +1,14 @@ +/** + * Class to hold information about the node attached to an input element. + * Used to control behavior based on node information. + */ +export class NodeInfo { + /** + * @param {string} nodeType - The type/class name of the node + * @param {string} inputName - The name of the input widget + */ + constructor(nodeType, inputName) { + this.nodeType = nodeType; + this.inputName = inputName; + } +} From 26772ac8dd7705c82b58abf0ee7394b64ad5e6d0 Mon Sep 17 00:00:00 2001 From: newtextdoc1111 Date: Fri, 21 Nov 2025 19:05:22 +0900 Subject: [PATCH 2/7] feat: Add manual formatting trigger and settings for auto-formatting --- web/js/auto-formatter.js | 26 +++++++++++++++++ web/js/main.js | 63 ++++++++++++++++++++++++++++++++++++---- web/js/settings.js | 1 + 3 files changed, 85 insertions(+), 5 deletions(-) diff --git a/web/js/auto-formatter.js b/web/js/auto-formatter.js index afc6bbe..d62cdb4 100644 --- a/web/js/auto-formatter.js +++ b/web/js/auto-formatter.js @@ -137,6 +137,7 @@ export class AutoFormatterEventHandler { handleBlur(event, nodeInfo) { if ( settingValues.enableAutoFormat && + settingValues.autoFormatTrigger === 'auto' && event.target.tagName === 'TEXTAREA' ) { const textarea = event.target; @@ -156,4 +157,29 @@ export class AutoFormatterEventHandler { 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; + } } diff --git a/web/js/main.js b/web/js/main.js index 29fcd1c..25b150a 100644 --- a/web/js/main.js +++ b/web/js/main.js @@ -13,16 +13,17 @@ import { NodeInfo } from "./node-info.js"; const id = "AutocompletePlus"; const name = "Autocomplete Plus"; +// --- Module-level variables --- +const autocompleteEventHandler = new AutocompleteEventHandler(); +const relatedTagsEventHandler = new RelatedTagsEventHandler(); +const autoFormatterEventHandler = new AutoFormatterEventHandler(); +const attachedElementNodeInfoMap = new WeakMap(); // Map to track attached elements and their node info + // --- Functions --- /** * Initialize event handlers for the autocomplete and related tags features. */ function initializeEventHandlers() { - const autocompleteEventHandler = new AutocompleteEventHandler(); - const relatedTagsEventHandler = new RelatedTagsEventHandler(); - const autoFormatterEventHandler = new AutoFormatterEventHandler(); - const attachedElementNodeInfoMap = new WeakMap(); // Map to track attached elements and their node info - // Function to attach listeners function attachListeners(element, nodeInfo) { if (attachedElementNodeInfoMap.has(element)) return; // Prevent double attachment @@ -302,6 +303,46 @@ app.registerExtension({ await loadDataAsync(); }, + // --- Commands --- + commands: [ + { + id: id + ".formatPrompt", + label: name + ": Format Prompt", + function: () => { + const activeEl = document.activeElement; + + if (!activeEl || activeEl.tagName !== 'TEXTAREA') { + console.debug('[Autocomplete-Plus] Format command: No textarea is currently focused'); + return; + } + + const nodeInfo = attachedElementNodeInfoMap.get(activeEl); + if (!nodeInfo) { + console.warn('[Autocomplete-Plus] Format command: Node info not found for focused textarea'); + // Use fallback NodeInfo + const fallbackNodeInfo = new NodeInfo('Unknown', 'unknown'); + autoFormatterEventHandler.applyFormatTextarea(activeEl, fallbackNodeInfo); + return; + } + + const formatted = autoFormatterEventHandler.applyFormatTextarea(activeEl, nodeInfo); + if (formatted) { + console.debug('[Autocomplete-Plus] Format command: Formatting applied'); + } else { + console.debug('[Autocomplete-Plus] Format command: Formatting skipped (blocklisted or not applicable)'); + } + } + } + ], + + // --- Keybindings --- + keybindings: [ + { + combo: { key: "f", alt: true, shift: true }, + commandId: id + ".formatPrompt" + } + ], + // One the Settings Screen, displays reverse order in same category settings: [ // --- Tag source Settings --- @@ -476,6 +517,18 @@ app.registerExtension({ }, // --- Auto format settings --- + { + id: id + '.AutoFormatter.Trigger', + name: 'Auto Format Trigger', + tooltip: 'Auto: Format automatically when leaving text field.\nManual: Format only via keyboard shortcut. default keybind: (Alt+Shift+F)', + type: 'combo', + options: ['auto', 'manual'], + defaultValue: 'auto', + category: [name, 'AutoFormatter', 'Auto Format Trigger'], + onChange: (newVal, oldVal) => { + settingValues.autoFormatTrigger = newVal; + }, + }, { id: id + '.AutoFormatter.EnableAutoFormat', name: 'Enable Auto Format', diff --git a/web/js/settings.js b/web/js/settings.js index 177b620..0cbc7b3 100644 --- a/web/js/settings.js +++ b/web/js/settings.js @@ -23,6 +23,7 @@ export const settingValues = { // Auto format settings enableAutoFormat: true, + autoFormatTrigger: 'auto', // Options: 'auto' (format on blur + shortcut), 'manual' (shortcut only) // Internal logic settings From e7eab66d8f4eda1eaed64dbd159ffb35b1c0cf15 Mon Sep 17 00:00:00 2001 From: newtextdoc1111 Date: Fri, 21 Nov 2025 19:25:35 +0900 Subject: [PATCH 3/7] feat: Enhance shouldAutoFormat logic to include single-letter placeholders in numeric checks --- web/js/auto-formatter.js | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/web/js/auto-formatter.js b/web/js/auto-formatter.js index d62cdb4..6858ee3 100644 --- a/web/js/auto-formatter.js +++ b/web/js/auto-formatter.js @@ -37,11 +37,17 @@ function shouldAutoFormat(text, nodeInfo) { const trimmedText = text.trim(); - // 2. Check if the text is purely numeric data with hyphens and commas - // (e.g., "0,0,0,1,1,1" or "0.5, -1.2, 0.8" for LoRA Block Weight) - const numericPattern = /^[\d.,\s-]+$/; - if (numericPattern.test(trimmedText)) { - return false; // Don't format numeric data + // 2. 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 } // 3. Check if the text contains the pattern "word + comma" @@ -183,3 +189,11 @@ export class AutoFormatterEventHandler { return false; } } + +// Export functions for testing +const isTestEnvironment = typeof process !== 'undefined' && process.env.NODE_ENV === 'test'; +export const __test__ = isTestEnvironment + ? { + shouldAutoFormat + } + : undefined; From 8459e3dac960afe08fb79384fe5ed93754bd179a Mon Sep 17 00:00:00 2001 From: newtextdoc1111 Date: Fri, 21 Nov 2025 19:25:44 +0900 Subject: [PATCH 4/7] test: Add unit tests for auto-formatting functions --- tests/js/auto-formatter.test.js | 90 +++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 tests/js/auto-formatter.test.js diff --git a/tests/js/auto-formatter.test.js b/tests/js/auto-formatter.test.js new file mode 100644 index 0000000..a4beff4 --- /dev/null +++ b/tests/js/auto-formatter.test.js @@ -0,0 +1,90 @@ +import { + formatPromptText, + __test__ +} from "../../web/js/auto-formatter.js"; + +const { + shouldAutoFormat +} = __test__; + +describe('AutoFormatter Functions', () => { + + describe('shouldAutoFormat', () => { + const mockNodeInfo = (nodeType, inputName) => ({ + nodeType, + inputName + }); + + test('should return false for empty text', () => { + expect(shouldAutoFormat('', mockNodeInfo('CLIPTextEncode', 'text'))).toBe(false); + expect(shouldAutoFormat(' ', mockNodeInfo('CLIPTextEncode', 'text'))).toBe(false); + }); + + test('should return false for blocklisted nodes', () => { + expect(shouldAutoFormat('some text,', mockNodeInfo('Power Puter (rgthree)', 'code'))).toBe(false); + expect(shouldAutoFormat('some text,', mockNodeInfo('LoraLoaderBlockWeight //Inspire', 'block_vector'))).toBe(false); + }); + + test('should return false for numeric data or single-letter placeholders', () => { + expect(shouldAutoFormat('0,0,0,1,1,1', mockNodeInfo('CLIPTextEncode', 'text'))).toBe(false); + expect(shouldAutoFormat('0.5, -1.2, 0.8', mockNodeInfo('CLIPTextEncode', 'text'))).toBe(false); + expect(shouldAutoFormat('A,B,R', mockNodeInfo('CLIPTextEncode', 'text'))).toBe(false); + expect(shouldAutoFormat('X, 1.5, Y', mockNodeInfo('CLIPTextEncode', 'text'))).toBe(false); + }); + + test('should return true for text with "word + comma" pattern', () => { + expect(shouldAutoFormat('1girl, blue hair,', mockNodeInfo('CLIPTextEncode', 'text'))).toBe(true); + expect(shouldAutoFormat('tag1, tag2', mockNodeInfo('CLIPTextEncode', 'text'))).toBe(true); + }); + + test('should return false if "word + comma" pattern is not found', () => { + expect(shouldAutoFormat('hello world', mockNodeInfo('CLIPTextEncode', 'text'))).toBe(false); + expect(shouldAutoFormat('tag1 tag2', mockNodeInfo('CLIPTextEncode', 'text'))).toBe(false); + }); + }); + + describe('formatPromptText', () => { + test('should format text by adding comma and space after tags', () => { + const input = 'tag1,tag2,tag3'; + const expected = 'tag1, tag2, tag3, '; + expect(formatPromptText(input)).toBe(expected); + }); + + test('should remove extra spaces around tags', () => { + const input = ' tag1 , tag2 '; + const expected = 'tag1, tag2, '; + expect(formatPromptText(input)).toBe(expected); + }); + + test('should preserve special syntax like weights', () => { + const input = '(tag1:1.2), [tag2]'; + // Note: The current implementation splits by comma. + // If the input is "(tag1:1.2), [tag2]", it splits into "(tag1:1.2)" and "[tag2]". + // Then joins with ", ". + const expected = '(tag1:1.2), [tag2], '; + expect(formatPromptText(input)).toBe(expected); + }); + + test('should handle multiple lines', () => { + const input = 'tag1, tag2\ntag3, tag4'; + const expected = 'tag1, tag2, \ntag3, tag4, '; + expect(formatPromptText(input)).toBe(expected); + }); + + test('should keep empty lines unchanged', () => { + const input = 'tag1, tag2\n\ntag3, tag4'; + const expected = 'tag1, tag2, \n\ntag3, tag4, '; + expect(formatPromptText(input)).toBe(expected); + }); + + test('should handle empty input', () => { + expect(formatPromptText('')).toBe(''); + expect(formatPromptText(null)).toBe(null); + expect(formatPromptText(undefined)).toBe(undefined); + }); + + test('should handle input with only spaces', () => { + expect(formatPromptText(' ')).toBe(' '); + }); + }); +}); From 38975c11aba80961c0379fac3bad6960cc47156c Mon Sep 17 00:00:00 2001 From: newtextdoc1111 Date: Sat, 22 Nov 2025 00:05:14 +0900 Subject: [PATCH 5/7] refactor: Extract node info retrieval into a separate function for cleaner event handlers --- web/js/main.js | 53 +++++++++++++------------------------------------- 1 file changed, 13 insertions(+), 40 deletions(-) diff --git a/web/js/main.js b/web/js/main.js index 25b150a..fbdb92c 100644 --- a/web/js/main.js +++ b/web/js/main.js @@ -102,36 +102,35 @@ function initializeEventHandlers() { observer.observe(document.body, { childList: true, subtree: true }); } - function handleInput(event) { + /** + * Get NodeInfo for the event target element + * @param {Event} event - The DOM event + * @returns {Object|null} NodeInfo object or undefined if not found + */ + function getNodeInfo(event) { const nodeInfo = attachedElementNodeInfoMap.get(event.target); if (!nodeInfo) { - console.warn('[Autocomplete-Plus] Node info not found for element in handleInput:', event.target); - return; + console.warn('[Autocomplete-Plus] Node info not found for element in ', event.target); + return null; } + return nodeInfo; + } + + function handleInput(event) { autocompleteEventHandler.handleInput(event); relatedTagsEventHandler.handleInput(event); autoFormatterEventHandler.handleInput(event); } function handleFocus(event) { - const nodeInfo = attachedElementNodeInfoMap.get(event.target); - if (!nodeInfo) { - console.warn('[Autocomplete-Plus] Node info not found for element in handleFocus:', event.target); - return; - } - autocompleteEventHandler.handleFocus(event); relatedTagsEventHandler.handleFocus(event); autoFormatterEventHandler.handleFocus(event); } function handleBlur(event) { - const nodeInfo = attachedElementNodeInfoMap.get(event.target); - if (!nodeInfo) { - console.warn('[Autocomplete-Plus] Node info not found for element in handleBlur:', event.target); - return; - } + const nodeInfo = getNodeInfo(event); // Get node info to pass to auto formatter autocompleteEventHandler.handleBlur(event); relatedTagsEventHandler.handleBlur(event); @@ -139,50 +138,24 @@ function initializeEventHandlers() { } function handleKeyDown(event) { - const nodeInfo = attachedElementNodeInfoMap.get(event.target); - if (!nodeInfo) { - console.warn('[Autocomplete-Plus] Node info not found for element in handleKeyDown:', event.target); - return; - } - autocompleteEventHandler.handleKeyDown(event); relatedTagsEventHandler.handleKeyDown(event); autoFormatterEventHandler.handleKeyDown(event); } function handleKeyUp(event) { - const nodeInfo = attachedElementNodeInfoMap.get(event.target); - if (!nodeInfo) { - console.warn('[Autocomplete-Plus] Node info not found for element in handleKeyUp:', event.target); - return; - } - autocompleteEventHandler.handleKeyUp(event); relatedTagsEventHandler.handleKeyUp(event); autoFormatterEventHandler.handleKeyUp(event); } - // New event handler for mousemove to show related tags on hover function handleMouseMove(event) { - const nodeInfo = attachedElementNodeInfoMap.get(event.target); - if (!nodeInfo) { - console.warn('[Autocomplete-Plus] Node info not found for element in handleMouseMove:', event.target); - return; - } - autocompleteEventHandler.handleMouseMove(event); relatedTagsEventHandler.handleMouseMove(event); autoFormatterEventHandler.handleMouseMove(event); } - // New event handler for click to show related tags function handleClick(event) { - const nodeInfo = attachedElementNodeInfoMap.get(event.target); - if (!nodeInfo) { - console.warn('[Autocomplete-Plus] Node info not found for element in handleClick:', event.target); - return; - } - autocompleteEventHandler.handleClick(event); relatedTagsEventHandler.handleClick(event); autoFormatterEventHandler.handleClick(event); From 9c570620115a6b6111b2cac22578e3ffcb8668e6 Mon Sep 17 00:00:00 2001 From: newtextdoc1111 Date: Sat, 22 Nov 2025 16:49:24 +0900 Subject: [PATCH 6/7] docs: Update shouldAutoFormat doc comment --- web/js/auto-formatter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/js/auto-formatter.js b/web/js/auto-formatter.js index 6858ee3..a47fc01 100644 --- a/web/js/auto-formatter.js +++ b/web/js/auto-formatter.js @@ -7,7 +7,7 @@ import { NodeInfo } from './node-info.js'; * * Format conditions: * 1. Skip formatting if node is in blocklist - * 2. Skip formatting if text contains only numbers, hyphens, commas, dots, and spaces + * 2. Skip formatting if text contains only numbers or single letters (separated by commas) * 3. Format if text contains "word + comma" pattern at least twice * 4. Otherwise, don't format * From 1c4e081f9ae0848fbc59a710040ceb2ecce88690 Mon Sep 17 00:00:00 2001 From: newtextdoc1111 Date: Sat, 22 Nov 2025 16:49:42 +0900 Subject: [PATCH 7/7] refactor: Comment out debug logs --- web/js/main.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/js/main.js b/web/js/main.js index fbdb92c..ad444a6 100644 --- a/web/js/main.js +++ b/web/js/main.js @@ -285,7 +285,7 @@ app.registerExtension({ const activeEl = document.activeElement; if (!activeEl || activeEl.tagName !== 'TEXTAREA') { - console.debug('[Autocomplete-Plus] Format command: No textarea is currently focused'); + // console.debug('[Autocomplete-Plus] Format command: No textarea is currently focused'); return; } @@ -300,9 +300,9 @@ app.registerExtension({ const formatted = autoFormatterEventHandler.applyFormatTextarea(activeEl, nodeInfo); if (formatted) { - console.debug('[Autocomplete-Plus] Format command: Formatting applied'); + // console.debug('[Autocomplete-Plus] Format command: Formatting applied'); } else { - console.debug('[Autocomplete-Plus] Format command: Formatting skipped (blocklisted or not applicable)'); + // console.debug('[Autocomplete-Plus] Format command: Formatting skipped (blocklisted or not applicable)'); } } }