| |
| import { app } from "../../scripts/app.js"; |
| import { ttN_CreateDropdown, ttN_RemoveDropdown } from "./ttN.js"; |
|
|
| |
| let embeddingsList = []; |
| let embeddingFiles = []; |
| let embeddingsHierarchy = {}; |
|
|
| |
| function convertListToHierarchy(list) { |
| const hierarchy = {}; |
|
|
| |
| for (var item of list) { |
| item = item.replace("embedding:", ""); |
| const parts = item.split(/:\\|\\/); |
| let currentNode = hierarchy; |
|
|
| |
| parts.forEach((part, index) => { |
| |
| if (index === parts.length - 1) { |
| currentNode[part] = null; |
| } else { |
| |
| currentNode[part] = currentNode[part] || {}; |
| currentNode = currentNode[part]; |
| } |
| }); |
| } |
|
|
| return hierarchy; |
| } |
|
|
| |
| app.registerExtension({ |
| name: "comfy.ttN.embeddingAC", |
| |
| async beforeRegisterNodeDef(nodeType, nodeData, app) { |
| |
| if (nodeData.name === "ttN pipeKSampler") { |
| initializeEmbeddingData(nodeData.input.hidden.embeddingsList[0]); |
| } |
| }, |
| |
| nodeCreated(node) { |
| |
| if (node.widgets && node.getTitle() !== "xyPlot") { |
| const relevantWidgets = filterRelevantWidgets(node.widgets); |
| addInputListenersToWidgets(relevantWidgets); |
| } |
| } |
| }); |
|
|
| |
| function filterRelevantWidgets(widgets) { |
| return widgets.filter(widget => (widget.type === "customtext" && widget.dynamicPrompts !== false) || widget.dynamicPrompts); |
| } |
|
|
| |
| function addInputListenersToWidgets(widgets) { |
| widgets.forEach(widget => { |
| const inputHandler = createWidgetInputHandler(widget); |
| setWidgetInputHandler(widget, inputHandler); |
| }); |
| } |
|
|
| |
| function createWidgetInputHandler(widget) { |
| return function handleInput() { |
| const currentWord = getCurrentWordFromInput(widget); |
| |
| if (shouldProvideEmbeddingSuggestion(currentWord)) { |
| const suggestions = filterEmbeddingsForInput(currentWord); |
| if (suggestions.length > 0) { |
| |
| embeddingsHierarchy = convertListToHierarchy(suggestions); |
| ttN_CreateDropdown(widget.inputEl, embeddingsHierarchy, selectedSuggestion => { |
| |
| widget.inputEl.value = updateInputWithSuggestion(widget.inputEl.value, selectedSuggestion, widget); |
| }, true); |
| return; |
| } |
| } |
| |
| ttN_RemoveDropdown(); |
| }; |
| } |
|
|
| |
| function setWidgetInputHandler(widget, handler) { |
| ['input', 'mousedown'].forEach(event => { |
| |
| widget.inputEl.removeEventListener(event, handler); |
| widget.inputEl.addEventListener(event, handler); |
| }); |
| } |
|
|
| |
| function getCurrentWordFromInput(widget) { |
| const cursorPosition = widget.inputEl.selectionStart; |
| const segments = widget.inputEl.value.split(' '); |
| return segments[widget.inputEl.value.substring(0, cursorPosition).split(' ').length - 1].toLowerCase(); |
| } |
|
|
| |
| function shouldProvideEmbeddingSuggestion(word) { |
| const suggestionPrefix = 'embedding:'; |
| return suggestionPrefix.startsWith(word) && word.length > 2 || word.startsWith(suggestionPrefix); |
| } |
|
|
| |
| function filterEmbeddingsForInput(input) { |
| const prefixes = ['embedding', 'embeddin', 'embeddi', 'embedd', 'embed', 'embe', 'emb'] |
|
|
| let inputLowered = input.toLowerCase(); |
| let cleanedInput = inputLowered.replace('embedding:', ''); |
| |
| prefixes.forEach(prefix => { |
| if (inputLowered.startsWith(prefix)) { |
| cleanedInput = cleanedInput.replace(prefix, ''); |
| } |
| }) |
|
|
| cleanedInput = cleanedInput.replace(/\//g, "\\"); |
|
|
| return embeddingsList.filter(embedding => { |
| const embeddingName = getFileName(embedding).toLowerCase(); |
| embedding = embedding.replace('embedding:', '').toLowerCase(); |
| if (embeddingName.startsWith(cleanedInput) || embedding.startsWith(cleanedInput) || prefixes.includes(cleanedInput)) { |
| return true; |
| } |
| return false |
| }); |
| } |
|
|
| function getFileName(path) { |
| const parts = path.split(/[\/:\\]/); |
| const fileName = parts[parts.length - 1]; |
| return fileName; |
| } |
|
|
| |
| function updateInputWithSuggestion(inputText, selectedSuggestion, widget) { |
| const cursorPosition = widget.inputEl.selectionStart; |
| const inputSegments = inputText.split(' '); |
| const cursorSegmentIndex = inputText.substring(0, cursorPosition).split(' ').length - 1; |
|
|
| if (inputSegments[cursorSegmentIndex].startsWith('emb')) { |
| inputSegments[cursorSegmentIndex] = 'embedding:' + selectedSuggestion; |
| } |
|
|
| return inputSegments.join(' '); |
| } |
|
|
| |
| function initializeEmbeddingData(initialEmbeddingsList) { |
| embeddingsList = initialEmbeddingsList; |
|
|
| embeddingsList.forEach(embedding => { |
| const fileName = embedding.split('\\').slice(-1)[0]; |
| embeddingFiles.push(fileName); |
| }); |
|
|
| embeddingsList = embeddingsList.map(embedding => { |
| const segments = embedding.split('/'); |
| return segments.map((segment, index) => "embedding:" + segments.slice(0, index + 1).join('/')); |
| }).flat(); |
| } |
|
|