Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8f6d451fef | ||
|
|
f2920add1d | ||
|
|
dbb5f66a1f | ||
|
|
cacee5ddad | ||
|
|
42f5e73515 | ||
|
|
52e5d2fd5f | ||
|
|
397c90275f | ||
|
|
2c8877060c | ||
|
|
1203e2f0ef | ||
|
|
f2ff83088d | ||
|
|
d6f53ebb0f | ||
|
|
2552e8d746 | ||
|
|
96ba7d8055 |
@@ -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
@@ -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+クリック)
|
||||
|
||||
### 表示
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"settingsCategories": {
|
||||
"Autocomplete Plus": "Autocomplete Plus",
|
||||
"Tag Source": "Tag Source",
|
||||
"Autocompletion": "Autocompletion",
|
||||
"Related Tags": "Related Tags",
|
||||
"Display": "Display"
|
||||
}
|
||||
}
|
||||
@@ -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."
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"settingsCategories": {
|
||||
"Autocomplete Plus": "Autocomplete Plus",
|
||||
"Tag Source": "タグソース",
|
||||
"Autocompletion": "オートコンプリート",
|
||||
"Related Tags": "関連タグ",
|
||||
"Display": "表示"
|
||||
}
|
||||
}
|
||||
@@ -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": "オートコンプリートと関連タグで表示されるエイリアス列を非表示にします"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"settingsCategories": {
|
||||
"Autocomplete Plus": "Autocomplete Plus",
|
||||
"Tag Source": "標籤來源",
|
||||
"Autocompletion": "自動完成",
|
||||
"Related Tags": "相關標籤",
|
||||
"Display": "顯示"
|
||||
}
|
||||
}
|
||||
@@ -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": "隱藏自動完成和相關標籤顯示中的別名欄"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"settingsCategories": {
|
||||
"Autocomplete Plus": "Autocomplete Plus",
|
||||
"Tag Source": "标签来源",
|
||||
"Autocompletion": "自动完成",
|
||||
"Related Tags": "相关标签",
|
||||
"Display": "显示"
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "comfyui-autocomplete-plus"
|
||||
description = "Autocomplete and Related Tag display for ComfyUI"
|
||||
version = "1.6.1"
|
||||
version = "1.7.2"
|
||||
license = {file = "LICENSE"}
|
||||
dependencies = ["",]
|
||||
|
||||
|
||||
@@ -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
@@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
+21
-13
@@ -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,27 @@ 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;
|
||||
tagRow.dataset.tagCategory = categoryText; // Used to color by CSS
|
||||
|
||||
// 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
@@ -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;
|
||||
}
|
||||
}
|
||||
},
|
||||
]
|
||||
});
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user