Compare commits

...
11 Commits
17 changed files with 530 additions and 44 deletions
+2 -1
View File
@@ -150,6 +150,7 @@ For example, by preparing the following CSV, you can quickly insert correspondin
- **Enable Autocomplete**: Enable/disable the autocomplete feature.
- **Max suggestions**: Maximum number of autocomplete suggestions to display.
- **Auto-Insert Comma**: Automatically insert a comma after tags when inserting from autocomplete.
- **Replace '_' with 'Space'**: Replaces underscores with spaces when inserting tags. This setting also affects related tag display.
- **Enable Loras and Embeddings**: Display Lora and Embedding in the suggestions.
- **Use Fast Search**: Switch autocomplete suggestions search to fast processing (see [About Fast Search for Autocomplete](#about-fast-search-for-autocomplete) for details).
@@ -159,7 +160,7 @@ For example, by preparing the following CSV, you can quickly insert correspondin
- **Enable Related Tags**: Enable/disable the related tags feature.
- **Max related tags**: Maximum number of related tags to display.
- **Default Display Position**: Default display position when ComfyUI starts.
- **Related Tags Trigger Mode**: Which action will trigger displaying related tags (click only, Ctrl+click)
- **Related Tags Trigger Mode**: Which action will trigger displaying related tags for the entered tag (click only, Ctrl+click)
### Display
+2 -1
View File
@@ -148,6 +148,7 @@ worst_quality,5,9999999,
- **Enable Autocomplete**: オートコンプリート機能の有効化/無効化
- **Max Suggestions**: オートコンプリート候補の最大表示件数
- **Auto-Insert Comma**: タグの挿入時、末尾にカンマを追加する
- **Replace '_' with 'Space'**: タグ挿入時にアンダースコアをスペースに置き換えます。この設定は関連タグ表示にも影響します
- **Enable Loras and Embeddings**: LoraとEmbeddingを候補に表示する
- **Use Fast Search**: オートコンプリート候補の検索を高速な処理に切り替える(詳細は [オートコンプリートの高速検索について](#オートコンプリートの高速検索について) を確認してください)
@@ -157,7 +158,7 @@ worst_quality,5,9999999,
- **Enable Related Tags**: 関連タグ機能の有効化/無効化
- **Max related tags**: 関連タグの最大表示件数
- **Default Display Position**: ComfyUI起動時のデフォルト表示位置
- **Related Tags Trigger Mode** : 関連タグを表示する際、どの操作をトリガーとするか(クリックのみ、Ctrl+クリック)
- **Related Tags Trigger Mode** : 入力済みのタグの関連タグを表示する際、どの操作をトリガーとするか(クリックのみ、Ctrl+クリック)
### 表示
+9
View File
@@ -0,0 +1,9 @@
{
"settingsCategories": {
"Autocomplete Plus": "Autocomplete Plus",
"Tag Source": "Tag Source",
"Autocompletion": "Autocompletion",
"Related Tags": "Related Tags",
"Display": "Display"
}
}
+74
View File
@@ -0,0 +1,74 @@
{
"AutocompletePlus_TagSource_IconPosition": {
"name": "Tag Source Icon Position",
"options": {
"left": "Left",
"right": "Right",
"hidden": "Hidden"
}
},
"AutocompletePlus_TagSource_PrimaryTagSource": {
"name": "Primary source for 'all' Source",
"tooltip": "When 'Autocomplete Tag Source' is 'all', this determines which source's tags appear first in suggestions.",
"options": {
"danbooru": "Danbooru",
"e621": "e621"
}
},
"AutocompletePlus_TagSource": {
"name": "Autocomplete Tag Source",
"tooltip": "Select the tag source for autocomplete suggestions. 'all' includes tags from all loaded sources.",
"options": {
"danbooru": "Danbooru",
"e621": "e621",
"all": "all"
}
},
"AutocompletePlus_Autocompletion_UseFastSearch": {
"name": "Use Fast Search",
"tooltip": "Tag search processing during text input operates faster, improving responsiveness."
},
"AutocompletePlus_Autocompletion_EnableModels": {
"name": "Enable Loras and Embeddings"
},
"AutocompletePlus_Autocompletion_ReplaceUnderscoreWithSpace": {
"name": "Replace '_' with 'Space'",
"tooltip": "This setting also affects related tags display."
},
"AutocompletePlus_Autocompletion_AutoInsertComma": {
"name": "Auto-Insert Comma",
"tooltip": "Automatically insert a comma after tags when inserting from autocomplete."
},
"AutocompletePlus_Autocompletion_MaxSuggestions": {
"name": "Max suggestions"
},
"AutocompletePlus_Autocompletion_Enable": {
"name": "Enable Autocomplete"
},
"AutocompletePlus_RelatedTags_RelatedTagsTriggerMode": {
"name": "Related Tags Trigger Mode",
"tooltip": "Which action will trigger displaying related tags for the entered tag (click only, Ctrl+click).",
"options": {
"click": "Click",
"ctrl+Click": "Ctrl+Click"
}
},
"AutocompletePlus_RelatedTags_DisplayPosition": {
"name": "Default Display Position",
"tooltip": "Display position (relative to Textarea).",
"options": {
"horizontal": "Horizontal",
"vertical": "Vertical"
}
},
"AutocompletePlus_RelatedTags_MaxRelatedTags": {
"name": "Max related tags"
},
"AutocompletePlus_RelatedTags_Enable": {
"name": "Enable Related Tags"
},
"AutocompletePlus_Display_HideAlias": {
"name": "Hide Alias",
"tooltip": "Hide alias column in the autocomplete and related tags display."
}
}
+9
View File
@@ -0,0 +1,9 @@
{
"settingsCategories": {
"Autocomplete Plus": "Autocomplete Plus",
"Tag Source": "タグソース",
"Autocompletion": "オートコンプリート",
"Related Tags": "関連タグ",
"Display": "表示"
}
}
+74
View File
@@ -0,0 +1,74 @@
{
"AutocompletePlus_TagSource_IconPosition": {
"name": "タグソースのアイコン位置",
"options": {
"left": "左",
"right": "右",
"hidden": "非表示"
}
},
"AutocompletePlus_TagSource_PrimaryTagSource": {
"name": "優先して表示するタグソース",
"tooltip": "オートコンプリートのタグソースが「全て」のとき、ここで指定したタグソースが優先して表示されます",
"options": {
"danbooru": "Danbooru",
"e621": "e621"
}
},
"AutocompletePlus_TagSource": {
"name": "オートコンプリートのタグソース",
"tooltip": "オートコンプリート候補に表示するタグソース。\n「全て」を選択するとロード済みの全てのタグソースを表示します",
"options": {
"danbooru": "Danbooru",
"e621": "e621",
"all": "全て"
}
},
"AutocompletePlus_Autocompletion_UseFastSearch": {
"name": "高速検索の有効化",
"tooltip": "候補の検索を高速な処理に切り替えます。\n入力時の遅延が改善される場合があります"
},
"AutocompletePlus_Autocompletion_EnableModels": {
"name": "LoraとEmbeddingを候補に表示する"
},
"AutocompletePlus_Autocompletion_ReplaceUnderscoreWithSpace": {
"name": "'_' を 'スペース' に置き換える",
"tooltip": "この設定は関連タグ表示にも影響します"
},
"AutocompletePlus_Autocompletion_AutoInsertComma": {
"name": "カンマを自動で追加する",
"tooltip": "オートコンプリート確定時、タグの末尾に自動でカンマを追加する"
},
"AutocompletePlus_Autocompletion_MaxSuggestions": {
"name": "候補の最大表示件数"
},
"AutocompletePlus_Autocompletion_Enable": {
"name": "オートコンプリート機能の有効化"
},
"AutocompletePlus_RelatedTags_RelatedTagsTriggerMode": {
"name": "関連タグを表示する操作",
"tooltip": "入力済みのタグの関連タグを表示する際、どの操作をトリガーとするか(クリックのみ、Ctrl+クリック)",
"options": {
"click": "クリック",
"ctrl+Click": "Ctrl+クリック"
}
},
"AutocompletePlus_RelatedTags_DisplayPosition": {
"name": "デフォルト表示位置",
"tooltip": "入力中のテキストエリアを基準に、どの位置に表示するか",
"options": {
"horizontal": "水平",
"vertical": "垂直"
}
},
"AutocompletePlus_RelatedTags_MaxRelatedTags": {
"name": "関連タグの最大表示件数"
},
"AutocompletePlus_RelatedTags_Enable": {
"name": "関連タグ機能の有効化"
},
"AutocompletePlus_Display_HideAlias": {
"name": "エイリアスを非表示",
"tooltip": "オートコンプリートと関連タグで表示されるエイリアス列を非表示にします"
}
}
+9
View File
@@ -0,0 +1,9 @@
{
"settingsCategories": {
"Autocomplete Plus": "Autocomplete Plus",
"Tag Source": "標籤來源",
"Autocompletion": "自動完成",
"Related Tags": "相關標籤",
"Display": "顯示"
}
}
+74
View File
@@ -0,0 +1,74 @@
{
"AutocompletePlus_TagSource_IconPosition": {
"name": "標籤來源圖示位置",
"options": {
"left": "左",
"right": "右",
"hidden": "隱藏"
}
},
"AutocompletePlus_TagSource_PrimaryTagSource": {
"name": "「全部」選擇時的主要來源",
"tooltip": "當自動完成標籤來源為「全部」時,此設定決定哪個來源的標籤優先顯示在建議中。",
"options": {
"danbooru": "Danbooru",
"e621": "e621"
}
},
"AutocompletePlus_TagSource": {
"name": "自動完成標籤來源",
"tooltip": "選擇自動完成建議的標籤來源。選擇「全部」將包含所有已載入來源的標籤。",
"options": {
"danbooru": "Danbooru",
"e621": "e621",
"all": "全部"
}
},
"AutocompletePlus_Autocompletion_UseFastSearch": {
"name": "啟用快速搜尋",
"tooltip": "標籤搜尋處理更快,提高文字輸入時的回應速度"
},
"AutocompletePlus_Autocompletion_EnableModels": {
"name": "顯示Lora和Embedding"
},
"AutocompletePlus_Autocompletion_ReplaceUnderscoreWithSpace": {
"name": "將'_'替換為'空格'",
"tooltip": "此設定也會影響相關標籤顯示"
},
"AutocompletePlus_Autocompletion_AutoInsertComma": {
"name": "自動插入逗號",
"tooltip": "從自動完成插入標籤時自動在標籤後添加逗號"
},
"AutocompletePlus_Autocompletion_MaxSuggestions": {
"name": "最大提示數"
},
"AutocompletePlus_Autocompletion_Enable": {
"name": "啟用自動完成"
},
"AutocompletePlus_RelatedTags_RelatedTagsTriggerMode": {
"name": "顯示相關標籤的操作",
"tooltip": "指定觸發顯示已輸入標籤的相關標籤的操作(僅點擊、Ctrl+點擊)",
"options": {
"click": "點擊",
"ctrl+Click": "Ctrl+點擊"
}
},
"AutocompletePlus_RelatedTags_DisplayPosition": {
"name": "預設顯示位置",
"tooltip": "相對於文字區域的顯示位置",
"options": {
"horizontal": "水平",
"vertical": "垂直"
}
},
"AutocompletePlus_RelatedTags_MaxRelatedTags": {
"name": "最大相關標籤數"
},
"AutocompletePlus_RelatedTags_Enable": {
"name": "啟用相關標籤"
},
"AutocompletePlus_Display_HideAlias": {
"name": "隱藏別名",
"tooltip": "隱藏自動完成和相關標籤顯示中的別名欄"
}
}
+9
View File
@@ -0,0 +1,9 @@
{
"settingsCategories": {
"Autocomplete Plus": "Autocomplete Plus",
"Tag Source": "标签来源",
"Autocompletion": "自动完成",
"Related Tags": "相关标签",
"Display": "显示"
}
}
+74
View File
@@ -0,0 +1,74 @@
{
"AutocompletePlus_TagSource_IconPosition": {
"name": "标签来源图标位置",
"options": {
"left": "左",
"right": "右",
"hidden": "隐藏"
}
},
"AutocompletePlus_TagSource_PrimaryTagSource": {
"name": "'全部'选择时的主要来源",
"tooltip": "当自动完成标签来源为\"全部\"时,此设置决定哪个来源的标签优先显示在建议中。",
"options": {
"danbooru": "Danbooru",
"e621": "e621"
}
},
"AutocompletePlus_TagSource": {
"name": "自动完成标签来源",
"tooltip": "选择自动完成建议的标签来源。选择\"全部\"将包含所有已加载来源的标签。",
"options": {
"danbooru": "Danbooru",
"e621": "e621",
"all": "全部"
}
},
"AutocompletePlus_Autocompletion_UseFastSearch": {
"name": "启用快速搜索",
"tooltip": "标签搜索处理更快,提高文本输入时的响应速度"
},
"AutocompletePlus_Autocompletion_EnableModels": {
"name": "显示Lora和Embedding"
},
"AutocompletePlus_Autocompletion_ReplaceUnderscoreWithSpace": {
"name": "将'_'替换为'空格'",
"tooltip": "此设置也会影响相关标签显示"
},
"AutocompletePlus_Autocompletion_AutoInsertComma": {
"name": "自动插入逗号",
"tooltip": "从自动完成插入标签时自动在标签后添加逗号"
},
"AutocompletePlus_Autocompletion_MaxSuggestions": {
"name": "最大提示数"
},
"AutocompletePlus_Autocompletion_Enable": {
"name": "启用自动完成"
},
"AutocompletePlus_RelatedTags_RelatedTagsTriggerMode": {
"name": "显示相关标签的操作",
"tooltip": "指定触发显示已输入标签的相关标签的操作(仅点击、Ctrl+点击)",
"options": {
"click": "点击",
"ctrl+Click": "Ctrl+点击"
}
},
"AutocompletePlus_RelatedTags_DisplayPosition": {
"name": "默认显示位置",
"tooltip": "相对于文本区域的显示位置",
"options": {
"horizontal": "水平",
"vertical": "垂直"
}
},
"AutocompletePlus_RelatedTags_MaxRelatedTags": {
"name": "最大相关标签数"
},
"AutocompletePlus_RelatedTags_Enable": {
"name": "启用相关标签"
},
"AutocompletePlus_Display_HideAlias": {
"name": "隐藏别名",
"tooltip": "隐藏自动完成和相关标签显示中的别名列"
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
[project]
name = "comfyui-autocomplete-plus"
description = "Autocomplete and Related Tag display for ComfyUI"
version = "1.6.1"
version = "1.7.1"
license = {file = "LICENSE"}
dependencies = ["",]
+12 -1
View File
@@ -353,7 +353,18 @@ describe('Autocomplete Functions', () => {
insertTagToTextArea(textarea, tagData);
}).not.toThrow();
expect(textarea.value).toBe('<lora:my_model>, ');
expect(textarea.value).toBe('<lora:my_model:1.0>, ');
});
test('should complete embedding tag without adding weight', () => {
const textarea = createMockTextarea('embedding:', 10);
const tagData = { tag: 'embedding:my_embedding', source: 'embeddings' };
expect(() => {
insertTagToTextArea(textarea, tagData);
}).not.toThrow();
expect(textarea.value).toBe('embedding:my_embedding, ');
});
test('should handle wildcard tag insertion', () => {
+100 -1
View File
@@ -4,7 +4,8 @@ import {
normalizeTagToSearch,
normalizeTagToInsert,
getCurrentTagRange,
findAllTagPositions
findAllTagPositions,
addWeightToLora
} from '../../web/js/utils.js';
// filepath: web/js/utils.test.js
@@ -440,3 +441,101 @@ describe('findAllTagPositions', () => {
expect(positions[5].tag).toBe('last');
});
});
describe('addWeightToModel', () => {
test('should return null or empty string for invalid inputs', () => {
expect(addWeightToLora(null)).toBeNull();
expect(addWeightToLora(undefined)).toBeUndefined();
expect(addWeightToLora('')).toBe('');
});
// LoRA format tests
test('should add default weight to LoRA without weight', () => {
expect(addWeightToLora('<lora:my_style01>')).toBe('<lora:my_style01:1.0>');
expect(addWeightToLora('<lora:anime_style>')).toBe('<lora:anime_style:1.0>');
expect(addWeightToLora('<lora:character_lora>')).toBe('<lora:character_lora:1.0>');
});
test('should preserve existing weight in LoRA', () => {
expect(addWeightToLora('<lora:my_style01:0.8>')).toBe('<lora:my_style01:0.8>');
expect(addWeightToLora('<lora:anime_style:1.2>')).toBe('<lora:anime_style:1.2>');
expect(addWeightToLora('<lora:character_lora:0.5>')).toBe('<lora:character_lora:0.5>');
});
test('should handle LoRA with path separators', () => {
expect(addWeightToLora('<lora:folder/my_style01>')).toBe('<lora:folder/my_style01:1.0>');
expect(addWeightToLora('<lora:folder/subfolder/style>')).toBe('<lora:folder/subfolder/style:1.0>');
expect(addWeightToLora('<lora:folder/my_style01:0.7>')).toBe('<lora:folder/my_style01:0.7>');
});
test('should handle LoRA with symbols', () => {
expect(addWeightToLora('<lora:folder/myLora style>')).toBe('<lora:folder/myLora style:1.0>');
expect(addWeightToLora('<lora:folder/myLora-style>')).toBe('<lora:folder/myLora-style:1.0>');
expect(addWeightToLora('<lora:folder/myLora[style]>')).toBe('<lora:folder/myLora[style]:1.0>');
expect(addWeightToLora('<lora:folder/myLora v1.0>')).toBe('<lora:folder/myLora v1.0:1.0>');
expect(addWeightToLora('<lora:folder/myLora v1.0:0.75>')).toBe('<lora:folder/myLora v1.0:0.75>');
});
test('should handle case-insensitive LoRA tags', () => {
expect(addWeightToLora('<LORA:my_style01>')).toBe('<LORA:my_style01:1.0>');
expect(addWeightToLora('<LoRa:anime_style>')).toBe('<LoRa:anime_style:1.0>');
expect(addWeightToLora('<Lora:character_lora:0.9>')).toBe('<Lora:character_lora:0.9>');
});
// Custom default weight tests
test('should use custom default weight for LoRA', () => {
expect(addWeightToLora('<lora:my_style01>', 0.5)).toBe('<lora:my_style01:0.5>');
expect(addWeightToLora('<lora:anime_style>', 1.5)).toBe('<lora:anime_style:1.5>');
expect(addWeightToLora('<lora:character_lora>', 2.0)).toBe('<lora:character_lora:2.0>');
});
test('should not override existing weight even with custom default', () => {
expect(addWeightToLora('<lora:my_style01:0.8>', 2.0)).toBe('<lora:my_style01:0.8>');
});
// Non-model format tests
test('should return non-model tags as-is', () => {
expect(addWeightToLora('blue_hair')).toBe('blue_hair');
expect(addWeightToLora('1girl')).toBe('1girl');
expect(addWeightToLora('standing:1.2')).toBe('standing:1.2');
expect(addWeightToLora('<other:tag>')).toBe('<other:tag>');
expect(addWeightToLora('random:text:format')).toBe('random:text:format');
});
// Edge cases
test('should handle malformed LoRA tags', () => {
expect(addWeightToLora('<lora:>')).toBe('<lora:>');
expect(addWeightToLora('<lora>')).toBe('<lora>');
expect(addWeightToLora('lora:my_style01')).toBe('lora:my_style01');
});
test('should handle decimal weights correctly', () => {
expect(addWeightToLora('<lora:style:0.123>')).toBe('<lora:style:0.123>');
expect(addWeightToLora('<lora:style:1.999>')).toBe('<lora:style:1.999>');
});
test('should handle special characters in model names', () => {
expect(addWeightToLora('<lora:my-style_01>')).toBe('<lora:my-style_01:1.0>');
expect(addWeightToLora('<lora:style.v2>')).toBe('<lora:style.v2:1.0>');
});
// Embedding format tests - should not be modified
test('should not modify embedding tags without weight', () => {
expect(addWeightToLora('embedding:my_embedding')).toBe('embedding:my_embedding');
expect(addWeightToLora('embedding:character_emb')).toBe('embedding:character_emb');
expect(addWeightToLora('embedding:style_embedding')).toBe('embedding:style_embedding');
});
test('should not modify embedding tags with existing weight', () => {
expect(addWeightToLora('embedding:my_embedding:0.8')).toBe('embedding:my_embedding:0.8');
expect(addWeightToLora('embedding:character_emb:1.2')).toBe('embedding:character_emb:1.2');
expect(addWeightToLora('embedding:style_embedding:0.5')).toBe('embedding:style_embedding:0.5');
});
test('should not modify embedding tags regardless of case', () => {
expect(addWeightToLora('EMBEDDING:my_embedding')).toBe('EMBEDDING:my_embedding');
expect(addWeightToLora('Embedding:character_emb')).toBe('Embedding:character_emb');
expect(addWeightToLora('EmBeDdInG:style_emb')).toBe('EmBeDdInG:style_emb');
expect(addWeightToLora('EMBEDDING:my_embedding:0.9')).toBe('EMBEDDING:my_embedding:0.9');
});
});
+20 -13
View File
@@ -18,7 +18,8 @@ import {
getCurrentTagRange,
getViewportMargin,
getScrollbarWidth,
IconSvgHtmlString
IconSvgHtmlString,
addWeightToLora
} from './utils.js';
import { settingValues } from './settings.js';
@@ -305,7 +306,7 @@ function getCurrentPartialTag(inputElement) {
* Inserts the selected tag into the textarea, replacing the partial tag,
* making the change undoable.
* @param {HTMLTextAreaElement} inputElement
* @param {TagData} tagDataToInsert The raw tag string to insert.
* @param {TagData} tagDataToInsert
*/
function insertTagToTextArea(inputElement, tagDataToInsert) {
if (!inputElement || !tagDataToInsert) {
@@ -331,8 +332,11 @@ function insertTagToTextArea(inputElement, tagDataToInsert) {
let replaceEnd = cursorPos;
let normalizedTag;
if (Object.values(ModelTagSource).includes(tagDataToInsert.source)) {
// If the tag is from a model tag source, don't want to normalize it
if (tagDataToInsert.source === ModelTagSource.Lora) {
// If the tag is from a LoRA source, add weight to it
normalizedTag = addWeightToLora(tagDataToInsert.tag);
} else if (Object.values(ModelTagSource).includes(tagDataToInsert.source)) {
// If the tag is from other model tag sources (e.g., Embeddings), don't normalize it
normalizedTag = tagDataToInsert.tag;
} else {
normalizedTag = normalizeTagToInsert(tagDataToInsert.tag);
@@ -347,9 +351,9 @@ function insertTagToTextArea(inputElement, tagDataToInsert) {
const needsSpaceBefore = text[replaceStart - 1] === ',';
const prefix = needsSpaceBefore ? ' ' : '';
// Standard separator (comma + space)
// Standard separator (comma + space, or empty if autoInsertComma is disabled)
const needsSuffixAfter = !",:".includes(text[replaceEnd]); // TODO: If ":" is part of the emoticon, a suffix is ​​required (e.g. ":o")
const suffix = needsSuffixAfter ? ', ' : '';
const suffix = (needsSuffixAfter && settingValues.autoInsertComma) ? ', ' : '';
const textToInsertWithAffixes = prefix + normalizedTag + suffix;
@@ -404,8 +408,11 @@ class AutocompleteUI {
// Add event listener for clicks on items
this.tagsList.addEventListener('mousedown', (e) => {
const row = e.target.closest('.autocomplete-plus-item');
if (row && row.dataset.tag) {
this.#insertTag(row.dataset);
if (row && row.dataset.index !== undefined) {
const tagData = this.candidates[parseInt(row.dataset.index, 10)];
if (tagData) {
this.#insertTag(tagData);
}
e.preventDefault(); // Prevent focus loss from input
e.stopPropagation();
}
@@ -496,26 +503,26 @@ class AutocompleteUI {
const existingTags = extractTagsFromTextArea(this.target);
const currentTag = getCurrentPartialTag(this.target);
this.candidates.forEach((tagData) => {
this.candidates.forEach((tagData, index) => {
const isExactMatch = tagData.tag === currentTag && (existingTags.filter(item => item === currentTag).length == 1);
const isExistingTag = !isExactMatch && existingTags.includes(tagData.tag);
this.#createTagElement(tagData, isExistingTag);
this.#createTagElement(tagData, index, isExistingTag);
});
}
/**
* Creates a tag element for the autocomplete list.
* @param {TagData} tagData
* @param {number} tagDataIndex
* @param {boolean} isExisting
*/
#createTagElement(tagData, isExisting) {
#createTagElement(tagData, tagDataIndex, isExisting) {
const categoryText = TagCategory[tagData.source][tagData.category] || "unknown";
const aliasText = tagData.alias.join(', ');
const tagRow = document.createElement('div');
tagRow.classList.add('autocomplete-plus-item', tagData.source);
tagRow.dataset.tag = tagData.tag;
tagRow.dataset.tagCategory = categoryText;
tagRow.dataset.index = tagDataIndex;
// Tag icon and name
const tagSourceIconHtml = `<svg class="autocomplete-plus-tag-icon-svg"><use xlink:href="#autocomplete-plus-icon-${tagData.source}"></use></svg>`;
+31 -24
View File
@@ -171,13 +171,13 @@ async function addExtraSettings() {
checkButton.disabled = false;
}
}
// Fetch last check time from API
let lastCheckTimeText = "Loading...";
try {
const response = await fetch('/autocomplete-plus/csv/last-check-time');
const data = await response.json();
if (data.last_check_time) {
const lastCheckDate = new Date(data.last_check_time);
lastCheckTimeText = "Last checked: " + lastCheckDate.toLocaleString();
@@ -248,7 +248,7 @@ app.registerExtension({
settings: [
// --- Tag source Settings ---
{
id: id + ".tag_source_icon_position",
id: id + ".TagSource.IconPosition",
name: "Tag Source Icon Position",
type: "combo",
options: ["left", "right", "hidden"],
@@ -259,7 +259,7 @@ app.registerExtension({
}
},
{
id: id + ".primary_tag_source",
id: id + ".TagSource.PrimaryTagSource",
name: "Primary source for 'all' Source",
tooltip: "When 'Autocomplete Tag Source' is 'all', this determines which source's tags appear first in suggestions.",
type: "combo",
@@ -271,7 +271,7 @@ app.registerExtension({
}
},
{
id: id + ".tag_source",
id: id + ".TagSource",
name: "Autocomplete Tag Source",
tooltip: "Select the tag source for autocomplete suggestions. 'all' includes tags from all loaded sources.",
type: "combo",
@@ -285,9 +285,9 @@ app.registerExtension({
// --- Autocomplete Settings ---
{
id: id + ".use_fast_search",
id: id + ".Autocompletion.UseFastSearch",
name: "Use Fast Search",
tooltip: "Tag search processing during text input operates faster, improving responsiveness",
tooltip: "Tag search processing during text input operates faster, improving responsiveness.",
type: "boolean",
defaultValue: false,
category: [name, "Autocompletion", "Use Fast Search"],
@@ -296,9 +296,8 @@ app.registerExtension({
}
},
{
id: id + ".enable_models",
id: id + ".Autocompletion.EnableModels",
name: "Enable Loras and Embeddings",
tooltip: "Enable Lora and Embedding suggestions",
type: "boolean",
defaultValue: true,
category: [name, "Autocompletion", "Enable Loras and Embeddings"],
@@ -307,7 +306,7 @@ app.registerExtension({
}
},
{
id: id + ".replace_underscore_with_space",
id: id + ".Autocompletion.ReplaceUnderscoreWithSpace",
name: "Replace '_' with 'Space'",
tooltip: "This setting also affects related tags display.",
type: "boolean",
@@ -318,7 +317,18 @@ app.registerExtension({
}
},
{
id: id + ".max_suggestions",
id: id + ".Autocompletion.AutoInsertComma",
name: "Auto-Insert Comma",
tooltip: "Automatically insert a comma after tags when inserting from autocomplete.",
type: "boolean",
defaultValue: true,
category: [name, "Autocompletion", "Auto-Insert Comma"],
onChange: (newVal, oldVal) => {
settingValues.autoInsertComma = newVal;
}
},
{
id: id + ".Autocompletion.MaxSuggestions",
name: "Max suggestions",
type: "slider",
attrs: {
@@ -333,9 +343,8 @@ app.registerExtension({
}
},
{
id: id + ".boolean",
id: id + ".Autocompletion.Enable",
name: "Enable Autocomplete",
description: "Enable or disable the autocomplete feature.",
type: "boolean",
defaultValue: true,
category: [name, "Autocompletion", "Enable Autocomplete"],
@@ -346,9 +355,9 @@ app.registerExtension({
// --- Related Tags Settings ---
{
id: id + ".related_tags_trigger_mode",
id: id + ".RelatedTags.RelatedTagsTriggerMode",
name: "Related Tags Trigger Mode",
description: "Trigger mode for related tags (click or ctrl+click)",
tooltip: "Which action will trigger displaying related tags for the entered tag (click only, Ctrl+click).",
type: "combo",
options: ["click", "ctrl+Click"],
defaultValue: "click",
@@ -358,9 +367,9 @@ app.registerExtension({
}
},
{
id: id + ".related_tags_position",
id: id + ".RelatedTags.DisplayPosition",
name: "Default Display Position",
description: "Display position (relative to Textarea)",
tooltip: "Display position (relative to Textarea).",
type: "combo",
options: ["horizontal", "vertical"],
defaultValue: "horizontal",
@@ -370,9 +379,8 @@ app.registerExtension({
}
},
{
id: id + ".max_related_tags",
id: id + ".RelatedTags.MaxRelatedTags",
name: "Max related tags",
description: "Maximum number of related tags to display",
type: "slider",
attrs: {
min: 5,
@@ -386,9 +394,8 @@ app.registerExtension({
}
},
{
id: id + ".related_tags_enable",
id: id + ".RelatedTags.Enable",
name: "Enable Related Tags",
description: "Enable or disable the related tags feature.",
type: "boolean",
defaultValue: true,
category: [name, "Related Tags", "Enable Related Tags"],
@@ -399,15 +406,15 @@ app.registerExtension({
// --- Display settings ---
{
id: id + ".display_hide_alias",
id: id + ".Display.HideAlias",
name: "Hide Alias",
tooltip: "Hide alias in the autocomplete and related tags display",
tooltip: "Hide alias column in the autocomplete and related tags display.",
type: "boolean",
defaultValue: false,
category: [name, "Display", "Hide Alias"],
onChange: (newVal, oldVal) => {
settingValues.hideAlias = newVal;
}
}
},
]
});
+1
View File
@@ -10,6 +10,7 @@ export const settingValues = {
enableModels: true, // Enable Lora and Embedding suggestions
useFastSearch: false,
replaceUnderscoreWithSpace: true, // Replace underscores with spaces in tag insertion
autoInsertComma: true,
// Related tags feature settings
enableRelatedTags: true,
+29 -2
View File
@@ -558,11 +558,38 @@ export function getCurrentTagRange(text, cursorPos) {
return { start: adjustedStart, end: adjustedEnd, tag: adjustedTag };
}
/**
* Adds weight to a lora tag if it doesn't already have one.
* @param {string} loraTag The model tag string (e.g., "<lora:my_style01>"
* @param {number} defaultWeight The default weight to add if not present
* @returns {string} The model tag with weight (e.g., "<lora:my_style01:1.0>")
*/
export function addWeightToLora(loraTag, defaultWeight = 1.0) {
if (!loraTag) return loraTag;
// Match LoRA format: <lora:name> or <lora:name:weight>
const loraMatch = loraTag.match(/^(<lora:[^>:]+)(:[0-9.]+)?>$/i);
if (loraMatch) {
const existingWeight = loraMatch[2];
// If weight already exists, return as-is
if (existingWeight) {
return loraTag;
}
// Add default weight, preserving original case
return `${loraMatch[1]}:${defaultWeight.toFixed(1)}>`;
}
// If it's not a recognized model format, return as-is
return loraTag;
}
// --- End String Helper Functions ---
/**
* Load a CSS file dynamically.
* @param {string} href
* @param {string} href
*/
export function loadCSS(href) {
const link = document.createElement('link');
@@ -624,6 +651,6 @@ export function getScrollbarWidth() {
_cachedScrollbarWidth = outer.offsetWidth - inner.offsetWidth;
document.body.removeChild(outer);
return _cachedScrollbarWidth;
}