Switch Model Resolver shortcut to native ComfyUI commands/keybindings with dynamic tooltip and exclude non-model categories

This commit is contained in:
Dariusz L
2026-07-04 04:34:34 +02:00
parent 1e22b2ba73
commit b57412f5cd
4 changed files with 190 additions and 12 deletions
+1
View File
@@ -4325,6 +4325,7 @@ class ModelResolverExtension:
import folder_paths
known_categories = set(folder_paths.folder_names_and_paths.keys())
skip_categories = {"custom_nodes", "configs"}
categories = get_enabled_download_categories(list(known_categories))
category_source_keys = {}
for cat in [*known_categories]:
File diff suppressed because one or more lines are too long
+15 -1
View File
@@ -5,7 +5,11 @@
*/
import { app } from "../../../scripts/app.js";
import { ModelResolver as ModelResolverClass } from "./resolver/model_resolver.js";
import {
MODEL_RESOLVER_OPEN_COMMAND_ID,
MODEL_RESOLVER_OPEN_DEFAULT_KEYBINDING,
ModelResolver as ModelResolverClass,
} from "./resolver/model_resolver.js";
import { registerGlobalHelpers } from "./resolver/globals.js";
registerGlobalHelpers();
@@ -14,5 +18,15 @@ const modelResolver = new ModelResolverClass();
app.registerExtension({
name: "Model Resolver",
commands: [
{
id: MODEL_RESOLVER_OPEN_COMMAND_ID,
label: "Open Model Resolver",
function: () => modelResolver.openResolverManager(),
},
],
keybindings: [
MODEL_RESOLVER_OPEN_DEFAULT_KEYBINDING,
],
setup: modelResolver.setup
});
+173 -10
View File
@@ -8,6 +8,70 @@ import { ResolverManagerDialog } from "./resolver_dialog.js";
import { showNotification } from "../utils/notification_utils.js";
const log = createModuleLogger('model_resolver');
export const MODEL_RESOLVER_OPEN_COMMAND_ID = "ModelResolver.OpenModelResolver";
export const MODEL_RESOLVER_OPEN_DEFAULT_KEYBINDING = Object.freeze({
commandId: MODEL_RESOLVER_OPEN_COMMAND_ID,
combo: Object.freeze({ key: "|", ctrl: true, shift: true }),
targetElementId: "graph-canvas",
});
const OPEN_TOOLTIP_BASE = "Open Model Resolver to find or download missing workflow models.";
const KEYBINDING_NEW_SETTING_ID = "Comfy.Keybinding.NewBindings";
const KEYBINDING_UNSET_SETTING_ID = "Comfy.Keybinding.UnsetBindings";
function getKeybindingSetting(id) {
try {
const value = app.ui?.settings?.getSettingValue?.(id);
return Array.isArray(value) ? value : [];
} catch (error) {
return [];
}
}
function serializeKeybindingCombo(combo = {}) {
return [
String(combo.key || "").toUpperCase(),
String(Boolean(combo.ctrl)),
String(Boolean(combo.alt)),
String(Boolean(combo.shift)),
].join(":");
}
function keybindingsEqual(left, right) {
return Boolean(
left
&& right
&& left.commandId === right.commandId
&& (left.targetElementId || "") === (right.targetElementId || "")
&& serializeKeybindingCombo(left.combo) === serializeKeybindingCombo(right.combo)
);
}
function getComboLabel(combo = {}) {
const parts = [];
if (combo.ctrl) parts.push("Ctrl");
if (combo.alt) parts.push("Alt");
if (combo.shift) parts.push("Shift");
const keyLabels = {
" ": "Space",
ArrowUp: "Up",
ArrowDown: "Down",
ArrowLeft: "Left",
ArrowRight: "Right",
Backspace: "Backspace",
Delete: "Delete",
Enter: "Enter",
Escape: "Esc",
Tab: "Tab",
};
const key = String(combo.key || "").trim();
if (key) {
parts.push(keyLabels[key] || (key.length === 1 ? key.toUpperCase() : key));
}
return parts.join("+");
}
function applyStoredFrontendLoggingPreference() {
const stored = localStorage.getItem('ModelResolver.frontendLogsEnabled');
@@ -28,7 +92,9 @@ export class ModelResolver {
this.buttonId = "model-resolver-button";
this.sidebarTabId = "comfyui-model-resolver";
this.sidebarRegistered = false;
this.openTooltip = "Open Model Resolver to find or download missing workflow models. Shortcut: Ctrl+Shift+L.";
this.openTooltipTargets = new Set();
this.openTooltip = this.buildOpenTooltip();
this.openTooltipRefreshFrame = null;
this.dialog = null;
this.isCheckingMissing = false; // Prevent multiple simultaneous checks
this.lastCheckedWorkflow = null; // Track to avoid duplicate checks
@@ -47,13 +113,7 @@ export class ModelResolver {
window.ModelResolverDialog = this.dialog;
}
// Register keyboard shortcut (Ctrl+Shift+L)
document.addEventListener('keydown', (e) => {
if (e.ctrlKey && e.shiftKey && e.key.toLowerCase() === 'l') {
e.preventDefault();
this.openResolverManager();
}
});
this.setupOpenShortcutTooltipTracking();
// Listen for workflow load events to auto-check for missing models
this.setupAutoOpenOnMissingModels();
@@ -107,6 +167,109 @@ export class ModelResolver {
document.addEventListener('click', window.__ModelResolverSidebarToggleHandler, true);
}
setupOpenShortcutTooltipTracking() {
window.__ModelResolverShortcutTooltipOwner = this;
this.refreshOpenTooltip();
if (window.__ModelResolverShortcutTooltipHandlersAttached) return;
window.__ModelResolverShortcutTooltipHandlersAttached = true;
const refreshHandler = () => {
window.__ModelResolverShortcutTooltipOwner?.scheduleOpenTooltipRefresh();
};
app.ui?.settings?.addEventListener?.(`${KEYBINDING_NEW_SETTING_ID}.change`, refreshHandler);
app.ui?.settings?.addEventListener?.(`${KEYBINDING_UNSET_SETTING_ID}.change`, refreshHandler);
window.addEventListener('storage', (event) => {
if (
event.key === KEYBINDING_NEW_SETTING_ID
|| event.key === KEYBINDING_UNSET_SETTING_ID
) {
refreshHandler();
}
});
}
scheduleOpenTooltipRefresh() {
if (this.openTooltipRefreshFrame) {
cancelAnimationFrame(this.openTooltipRefreshFrame);
}
this.openTooltipRefreshFrame = requestAnimationFrame(() => {
this.openTooltipRefreshFrame = null;
this.refreshOpenTooltip();
});
}
getOpenKeybindings() {
const byCombo = {
[serializeKeybindingCombo(MODEL_RESOLVER_OPEN_DEFAULT_KEYBINDING.combo)]: MODEL_RESOLVER_OPEN_DEFAULT_KEYBINDING,
};
for (const keybinding of getKeybindingSetting(KEYBINDING_UNSET_SETTING_ID)) {
const serializedCombo = serializeKeybindingCombo(keybinding?.combo);
if (keybindingsEqual(byCombo[serializedCombo], keybinding)) {
delete byCombo[serializedCombo];
}
}
for (const keybinding of getKeybindingSetting(KEYBINDING_NEW_SETTING_ID)) {
if (!keybinding?.combo) continue;
byCombo[serializeKeybindingCombo(keybinding.combo)] = keybinding;
}
return Object.values(byCombo)
.filter((keybinding) => keybinding?.commandId === MODEL_RESOLVER_OPEN_COMMAND_ID);
}
buildOpenTooltip() {
const shortcutLabels = this.getOpenKeybindings()
.map((keybinding) => getComboLabel(keybinding.combo))
.filter(Boolean);
if (shortcutLabels.length === 0) {
return `${OPEN_TOOLTIP_BASE} Shortcut: not assigned.`;
}
const label = shortcutLabels.length === 1 ? "Shortcut" : "Shortcuts";
return `${OPEN_TOOLTIP_BASE} ${label}: ${shortcutLabels.join(", ")}.`;
}
refreshOpenTooltip() {
this.openTooltip = this.buildOpenTooltip();
this.applyOpenTooltipToTargets();
}
trackOpenTooltipTarget(target) {
if (!(target instanceof HTMLElement)) return;
this.openTooltipTargets.add(target);
this.applyTooltipToElement(target);
}
applyTooltipToElement(target) {
if (!(target instanceof HTMLElement)) return;
if (this.dialog?.setTooltip) {
this.dialog.setTooltip(target, this.openTooltip);
} else {
target.setAttribute("data-tooltip", this.openTooltip);
target.removeAttribute("title");
}
}
applyOpenTooltipToTargets() {
const liveTargets = new Set();
for (const target of this.openTooltipTargets) {
if (!(target instanceof HTMLElement) || !target.isConnected) continue;
liveTargets.add(target);
this.applyTooltipToElement(target);
}
this.openTooltipTargets = liveTargets;
document.querySelectorAll(`.${this.sidebarTabId}-tab-button`).forEach((button) => {
if (button instanceof HTMLElement) {
this.applyTooltipToElement(button);
}
});
}
handleSidebarButtonClick(event) {
const target = event.target instanceof Element ? event.target : null;
const button = target?.closest(`.${this.sidebarTabId}-tab-button`);
@@ -184,7 +347,7 @@ export class ModelResolver {
classList: "comfyui-button comfyui-menu-mobile-collapse"
}).element;
ModelResolverButton.id = this.buttonId;
this.dialog.setTooltip(ModelResolverButton, this.openTooltip);
this.trackOpenTooltipTarget(ModelResolverButton);
this.buttonGroup = new ComfyButtonGroup(
ModelResolverButton
);
@@ -724,7 +887,7 @@ export class ModelResolver {
});
document.body.appendChild(this.resolverButton);
this.dialog?.setTooltip(this.resolverButton, "Open Model Resolver to find or download missing workflow models. Shortcut: Ctrl+Shift+L.");
this.trackOpenTooltipTarget(this.resolverButton);
}
async openResolverManager(options = {}) {