diff --git a/tests/js/auto-formatter.test.js b/tests/js/auto-formatter.test.js index 3281a5a..78acd61 100644 --- a/tests/js/auto-formatter.test.js +++ b/tests/js/auto-formatter.test.js @@ -41,6 +41,38 @@ describe('AutoFormatter Functions', () => { expect(shouldAutoFormat('X, 1.5, Y', mockNodeInfo('CLIPTextEncode', 'text'))).toBe(false); }); + test('should return false for JSON-like content (avoid corrupting JSON commas)', () => { + const jsonText = `{ + "quality_meta_year_safe": "masterpiece, best quality, score_9, year 2025, safe", + "count": "1girl", + "artist": "{{artist}}", + "tags": "tag1, tag2", + "width": 1024 +}`; + + // JSON contains many "word + comma" patterns inside string values, + // but it must never be auto-formatted as prompt tags. + expect(shouldAutoFormat(jsonText, mockNodeInfo('SimpleChatTextInput', 'text'))).toBe(false); + }); + + test('should return true for prompts containing comfyui-prompt-control scheduling syntax', () => { + // Range expression: [before:during:after:start,end] + expect(shouldAutoFormat('[black::white:0.3,0.7], 1girl, blue hair', mockNodeInfo('CLIPTextEncode', 'text'))).toBe(true); + + // Alternating: [a|b:pct] + expect(shouldAutoFormat('[cat|dog:0.1], 1girl, solo', mockNodeInfo('CLIPTextEncode', 'text'))).toBe(true); + + // Sequence: [SEQ:a:N1:b:N2:...] + expect(shouldAutoFormat('[SEQ:red:0.3:blue:0.6:green:0.9], 1girl', mockNodeInfo('CLIPTextEncode', 'text'))).toBe(true); + + // LoRA scheduling + expect(shouldAutoFormat(' [::0.5], 1girl', mockNodeInfo('CLIPTextEncode', 'text'))).toBe(true); + + // Nested scheduling + expect(shouldAutoFormat('[red:[blue::0.7]:0.5] cat, 1girl', mockNodeInfo('CLIPTextEncode', 'text'))).toBe(true); + }); + + 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); diff --git a/web/js/auto-formatter.js b/web/js/auto-formatter.js index 9c586ae..8a76993 100644 --- a/web/js/auto-formatter.js +++ b/web/js/auto-formatter.js @@ -1,6 +1,26 @@ import { settingValues } from './settings.js'; import { NodeInfo } from './node-info.js'; +/** + * JSON content detection with actual parsing. + * + * @param {string} text - The text to check for JSON content. + * @return {boolean} - True if the text is valid JSON, false otherwise. + */ +function isJsonContent(text) { + if (!text) return false; + const t = text.trim(); + if (t.length < 2) return false; + if (t[0] !== '{' && t[0] !== '[') return false; + + try { + JSON.parse(t); + return true; + } catch { + return false; + } +} + /** * Determines if the text content should be auto-formatted. @@ -8,10 +28,11 @@ import { NodeInfo } from './node-info.js'; * 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 + * 3. Skip formatting if text is valid JSON + * 4. Skip formatting if text contains only numbers or single letters (separated by commas) + * 5. Format if text contains "word + comma" pattern + * 6. Format if trimSurroundingSpaces is enabled and there are surrounding spaces or line breaks + * 7. Otherwise, don't format * * @param {NodeInfo} nodeInfo - The node information. * @returns {boolean} - True if the text should be formatted, false otherwise. @@ -43,7 +64,12 @@ function shouldAutoFormat(text, nodeInfo) { return settingValues.trimSurroundingSpaces; } - // 3. Check if the text is purely numeric data or single-letter placeholders with commas + // 3. Skip JSON content (do not destroy JSON commas) + if (isJsonContent(trimmedText)) { + return false; + } + + // 4. 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 => { @@ -56,13 +82,13 @@ function shouldAutoFormat(text, nodeInfo) { 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; + // 5. If text contains "word + comma" pattern, format it + const wordCommaPattern = /[^\s,]+\s*,/g; if (trimmedText.match(wordCommaPattern)) { return true; } - // 5. If trimSurroundingSpaces is enabled and there are surrounding spaces, format to trim them + // 6. If trimSurroundingSpaces is enabled and there are surrounding spaces, format to trim them if (settingValues.trimSurroundingSpaces && text !== trimmedText) { return true; }