Files
MajoorWaldi-ComfyUI-Majoor-…/js/features/panel/controllers/contextController.js
T
MajoorWaldi 2fa41a0e76 feat: add useGridState and useVirtualGrid composables for asset management
- Implemented useGridState to manage grid state, including asset selection and loading states.
- Created useVirtualGrid to centralize virtualization helpers for improved performance.
- Added createVueApp for isolated Vue 3 + Pinia application instances.
- Introduced Vite configuration for building the ComfyUI-Majoor-AssetsManager with external dependencies.
2026-04-03 11:04:55 +02:00

493 lines
16 KiB
JavaScript

import { normalizeQuery } from "./query.js";
import { APP_CONFIG } from "../../../app/config.js";
import { createPanelStateBridge } from "../../../stores/panelStateBridge.js";
const _safeSetValue = (el, value) => {
if (!el) return;
try {
el.value = value;
} catch (e) {
console.debug?.(e);
}
};
const _safeSetChecked = (el, checked) => {
if (!el) return;
try {
el.checked = !!checked;
} catch (e) {
console.debug?.(e);
}
};
export function createContextController({
state,
gridContainer,
filterBtn,
sortBtn,
collectionsBtn,
searchInputEl,
kindSelect,
wfCheckbox,
workflowTypeSelect,
ratingSelect,
minSizeInput,
maxSizeInput,
minWidthInput,
minHeightInput,
maxWidthInput,
maxHeightInput,
resolutionPresetSelect,
dateRangeSelect,
dateExactInput,
scopeController,
sortController,
updateSummaryBar,
reloadGrid,
extraActions = null,
getExtraContext = null,
} = {}) {
const legacyState = state && typeof state === "object" ? state : {};
const { read, write, controllerState } = createPanelStateBridge(state, [
"searchQuery",
"scope",
"collectionId",
"collectionName",
"customRootId",
"customRootLabel",
"currentFolderRelativePath",
"kindFilter",
"workflowOnly",
"minRating",
"minSizeMB",
"maxSizeMB",
"minWidth",
"minHeight",
"maxWidth",
"maxHeight",
"resolutionCompare",
"workflowType",
"dateRangeFilter",
"dateExactFilter",
"sort",
"viewScope",
"similarResults",
"similarTitle",
"similarSourceAssetId",
]);
const setStateValue = (key, value) => {
write(key, value);
};
const getStateValue = (key, fallback = "") => {
return read(key, fallback);
};
const getContextState = () => ({
...(legacyState || {}),
...(controllerState?.() || {}),
customRootLabel: getStateValue("customRootLabel", ""),
});
const resetBrowserHistory = () => {
try {
extraActions?.resetBrowserHistory?.();
} catch (e) {
console.debug?.(e);
}
};
const applyBadgeCssVars = () => {
try {
const root =
gridContainer?.closest?.(".mjr-assets-manager") ||
document.querySelector?.(".mjr-assets-manager") ||
document.documentElement;
if (!root?.style?.setProperty) return;
root.style.setProperty(
"--mjr-star-active",
String(APP_CONFIG.BADGE_STAR_COLOR || "#FFD45A"),
);
root.style.setProperty(
"--mjr-badge-image",
String(APP_CONFIG.BADGE_IMAGE_COLOR || "#2196F3"),
);
root.style.setProperty(
"--mjr-badge-video",
String(APP_CONFIG.BADGE_VIDEO_COLOR || "#9C27B0"),
);
root.style.setProperty(
"--mjr-badge-audio",
String(APP_CONFIG.BADGE_AUDIO_COLOR || "#FF9800"),
);
root.style.setProperty(
"--mjr-badge-model3d",
String(APP_CONFIG.BADGE_MODEL3D_COLOR || "#4CAF50"),
);
root.style.setProperty(
"--mjr-badge-duplicate-alert",
String(APP_CONFIG.BADGE_DUPLICATE_ALERT_COLOR || "#FF1744"),
);
} catch (e) {
console.debug?.(e);
}
};
const actions = {
clearQuery: async () => {
try {
_safeSetValue(searchInputEl, "");
setStateValue("searchQuery", "");
} catch (e) {
console.debug?.(e);
}
try {
await reloadGrid?.();
} catch (e) {
console.debug?.(e);
}
},
clearCollection: async () => {
try {
setStateValue("collectionId", "");
setStateValue("collectionName", "");
} catch (e) {
console.debug?.(e);
}
try {
await reloadGrid?.();
} catch (e) {
console.debug?.(e);
}
},
clearScope: async () => {
try {
if (
String(state?.viewScope || "")
.trim()
.toLowerCase() === "similar" &&
typeof extraActions?.clearSimilarScope === "function"
) {
await extraActions.clearSimilarScope();
return;
}
} catch (e) {
console.debug?.(e);
}
let didSetScope = false;
try {
setStateValue("customRootId", "");
setStateValue("customRootLabel", "");
setStateValue("currentFolderRelativePath", "");
delete gridContainer?.dataset?.mjrSubfolder;
resetBrowserHistory();
if (typeof scopeController?.setScope === "function") {
await scopeController.setScope("output");
didSetScope = true;
} else {
setStateValue("scope", "output");
scopeController?.setActiveTabStyles?.();
}
} catch (e) {
console.debug?.(e);
}
try {
if (!didSetScope) await reloadGrid?.();
} catch (e) {
console.debug?.(e);
}
},
clearCustomRoot: async () => {
try {
setStateValue("customRootId", "");
setStateValue("customRootLabel", "");
setStateValue("currentFolderRelativePath", "");
delete gridContainer?.dataset?.mjrSubfolder;
resetBrowserHistory();
} catch (e) {
console.debug?.(e);
}
try {
await reloadGrid?.();
} catch (e) {
console.debug?.(e);
}
},
clearFolder: async () => {
try {
// Ensure state is cleared so it doesn't get restored
setStateValue("currentFolderRelativePath", "");
delete gridContainer?.dataset?.mjrSubfolder;
resetBrowserHistory();
} catch (e) {
console.debug?.(e);
}
try {
await reloadGrid?.();
} catch (e) {
console.debug?.(e);
}
},
clearKind: async () => {
try {
setStateValue("kindFilter", "");
_safeSetValue(kindSelect, "");
} catch (e) {
console.debug?.(e);
}
try {
await reloadGrid?.();
} catch (e) {
console.debug?.(e);
}
},
clearMinRating: async () => {
try {
setStateValue("minRating", 0);
_safeSetValue(ratingSelect, "0");
} catch (e) {
console.debug?.(e);
}
try {
await reloadGrid?.();
} catch (e) {
console.debug?.(e);
}
},
clearWorkflowOnly: async () => {
try {
setStateValue("workflowOnly", false);
_safeSetChecked(wfCheckbox, false);
} catch (e) {
console.debug?.(e);
}
try {
await reloadGrid?.();
} catch (e) {
console.debug?.(e);
}
},
clearWorkflowType: async () => {
try {
setStateValue("workflowType", "");
_safeSetValue(workflowTypeSelect, "");
} catch (e) {
console.debug?.(e);
}
try {
await reloadGrid?.();
} catch (e) {
console.debug?.(e);
}
},
clearSize: async () => {
try {
setStateValue("minSizeMB", 0);
setStateValue("maxSizeMB", 0);
_safeSetValue(minSizeInput, "");
_safeSetValue(maxSizeInput, "");
} catch (e) {
console.debug?.(e);
}
try {
await reloadGrid?.();
} catch (e) {
console.debug?.(e);
}
},
clearResolution: async () => {
try {
setStateValue("minWidth", 0);
setStateValue("minHeight", 0);
setStateValue("maxWidth", 0);
setStateValue("maxHeight", 0);
_safeSetValue(minWidthInput, "");
_safeSetValue(minHeightInput, "");
_safeSetValue(maxWidthInput, "");
_safeSetValue(maxHeightInput, "");
_safeSetValue(resolutionPresetSelect, "");
} catch (e) {
console.debug?.(e);
}
try {
await reloadGrid?.();
} catch (e) {
console.debug?.(e);
}
},
clearDateRange: async () => {
try {
setStateValue("dateRangeFilter", "");
_safeSetValue(dateRangeSelect, "");
} catch (e) {
console.debug?.(e);
}
try {
await reloadGrid?.();
} catch (e) {
console.debug?.(e);
}
},
clearDateExact: async () => {
try {
setStateValue("dateExactFilter", "");
_safeSetValue(dateExactInput, "");
} catch (e) {
console.debug?.(e);
}
try {
await reloadGrid?.();
} catch (e) {
console.debug?.(e);
}
},
clearSort: async () => {
try {
setStateValue("sort", "mtime_desc");
sortController?.setSortIcon?.(getStateValue("sort", "mtime_desc"));
sortController?.renderSortMenu?.();
} catch (e) {
console.debug?.(e);
}
try {
await reloadGrid?.();
} catch (e) {
console.debug?.(e);
}
},
clearAll: async () => {
let didSetScope = false;
try {
_safeSetValue(searchInputEl, "");
} catch (e) {
console.debug?.(e);
}
try {
setStateValue("collectionId", "");
setStateValue("collectionName", "");
setStateValue("kindFilter", "");
setStateValue("workflowOnly", false);
setStateValue("minRating", 0);
setStateValue("minSizeMB", 0);
setStateValue("maxSizeMB", 0);
setStateValue("minWidth", 0);
setStateValue("minHeight", 0);
setStateValue("maxWidth", 0);
setStateValue("maxHeight", 0);
setStateValue("resolutionCompare", "gte");
setStateValue("workflowType", "");
setStateValue("dateRangeFilter", "");
setStateValue("dateExactFilter", "");
setStateValue("sort", "mtime_desc");
setStateValue("customRootId", "");
setStateValue("customRootLabel", "");
setStateValue("currentFolderRelativePath", "");
delete gridContainer?.dataset?.mjrSubfolder;
resetBrowserHistory();
try {
await extraActions?.clearTransientContext?.();
} catch (e) {
console.debug?.(e);
}
if (typeof scopeController?.setScope === "function") {
await scopeController.setScope("output");
didSetScope = true;
} else {
setStateValue("scope", "output");
scopeController?.setActiveTabStyles?.();
}
} catch (e) {
console.debug?.(e);
}
try {
_safeSetValue(kindSelect, "");
_safeSetChecked(wfCheckbox, false);
_safeSetValue(workflowTypeSelect, "");
_safeSetValue(ratingSelect, "0");
_safeSetValue(minSizeInput, "");
_safeSetValue(maxSizeInput, "");
_safeSetValue(minWidthInput, "");
_safeSetValue(minHeightInput, "");
_safeSetValue(maxWidthInput, "");
_safeSetValue(maxHeightInput, "");
_safeSetValue(resolutionPresetSelect, "");
_safeSetValue(dateRangeSelect, "");
_safeSetValue(dateExactInput, "");
} catch (e) {
console.debug?.(e);
}
try {
sortController?.setSortIcon?.(getStateValue("sort", "mtime_desc"));
sortController?.renderSortMenu?.();
} catch (e) {
console.debug?.(e);
}
try {
if (!didSetScope) await reloadGrid?.();
} catch (e) {
console.debug?.(e);
}
},
};
if (extraActions && typeof extraActions === "object") {
const blocked = new Set(["__proto__", "constructor", "prototype"]);
for (const [key, value] of Object.entries(extraActions)) {
if (blocked.has(String(key))) continue;
actions[key] = value;
}
}
const update = () => {
const rawQuery = String(searchInputEl?.value || "").trim();
const normalizedQuery = normalizeQuery(searchInputEl);
const currentState = getContextState();
const filterActive = !!(
getStateValue("kindFilter", "") ||
"" ||
getStateValue("workflowOnly", false) ||
(Number(getStateValue("minRating", 0) || 0) || 0) > 0 ||
(Number(getStateValue("minSizeMB", 0) || 0) || 0) > 0 ||
(Number(getStateValue("maxSizeMB", 0) || 0) || 0) > 0 ||
(Number(getStateValue("minWidth", 0) || 0) || 0) > 0 ||
(Number(getStateValue("minHeight", 0) || 0) || 0) > 0 ||
(Number(getStateValue("maxWidth", 0) || 0) || 0) > 0 ||
(Number(getStateValue("maxHeight", 0) || 0) || 0) > 0 ||
String(getStateValue("workflowType", "") || "").trim().length > 0 ||
getStateValue("dateRangeFilter", "") ||
"" ||
getStateValue("dateExactFilter", "") ||
""
);
const sortActive = String(getStateValue("sort", "mtime_desc") || "mtime_desc") !== "mtime_desc";
const collectionsActive = !!(getStateValue("collectionId", "") || "");
try {
filterBtn?.classList?.toggle?.("mjr-context-active", filterActive);
sortBtn?.classList?.toggle?.("mjr-context-active", sortActive);
collectionsBtn?.classList?.toggle?.("mjr-context-active", collectionsActive);
} catch (e) {
console.debug?.(e);
}
try {
const extraContext =
(typeof getExtraContext === "function" ? getExtraContext() || {} : {}) || {};
updateSummaryBar?.({
state: currentState,
gridContainer,
context: { rawQuery: rawQuery || normalizedQuery, ...extraContext },
actions,
});
} catch (e) {
console.debug?.(e);
}
// Keep badge CSS vars aligned with live settings/theme changes.
applyBadgeCssVars();
};
return { update, actions };
}