Files
willmiao-ComfyUI-Lora-Manager/static/js/workflows_disabled.js
T
Will Miao 720440507c feat: add opt-in workflow library management
Add a Workflows page that manages ComfyUI workflow JSON files as a
first-class library alongside loras/checkpoints/embeddings:

- opt-in via the new 'Workflow Management' settings toggle (default off)
- dedicated workflow folder roots with sidecar/preview support, separate
  from ComfyUI's own workflow storage; 'Send to ComfyUI' loads a workflow
  straight into the graph
- workflow scanner parses the graph document (UI/API format sniffing,
  node classes, model/civitai refs) and persists the analysis in the
  SQLite model cache
- settings UI: workflow folder paths editor in the Library tab and the
  standalone Model Paths page

Fixes from sandbox E2E verification:

- include the workflow scanner in startup initialization so the page no
  longer hangs on 'initializing' when enabled without folders
- add the missing workflows entry to the frontend page-state map (page
  init crashed and the sidebar fell back to the loras tree without it)
- project workflow analysis fields through the scanner cache entry and
  persist them in the model cache (new columns with auto-migration)
- keep symlinked paths intact in the directory picker (abspath instead
  of resolve), so stored library paths match what the user sees on disk

Also rebuilds the vue-widgets bundle to sync it with web/comfyui/utils.js.
2026-10-28 11:45:01 +08:00

110 lines
4.2 KiB
JavaScript

import { appCore } from './core.js';
import { showToast } from './utils/uiHelpers.js';
import { enableWorkflows, openWorkflowsSettings, openModelPathsSettings } from './utils/workflowModels.js';
/**
* Workflow management is an opt-in feature. While it is disabled this page
* renders an empty state whose button turns the feature on; the backend then
* rebuilds the workflow roots and starts scanning, so a reload lands on the
* real page.
*
* The same module backs the "enabled but no folders found" state: ComfyUI
* mode points to the Settings page's Library section, while standalone mode
* points to the standalone-only Model Paths section (which edits the primary
* folder_paths) and still offers the settings.json location as a fallback.
*/
async function handleEnableClick() {
const button = document.getElementById('enableWorkflowsBtn');
if (!button || button.disabled) return;
button.disabled = true;
try {
await enableWorkflows();
} catch (error) {
button.disabled = false;
showToast('workflows.disabled.enableFailed', { message: error.message }, 'error');
}
}
/**
* Open Settings on the Library section for the "no folders found" state, so a
* misconfigured install can be fixed without hand-editing unknown keys.
*/
function handleOpenSettingsClick(event) {
event.preventDefault();
openWorkflowsSettings();
}
/**
* Open Settings on the Model Paths section for the standalone "no folders
* found" state, so the missing folders can be added directly.
*/
function handleOpenModelPathsSettingsClick(event) {
event.preventDefault();
openModelPathsSettings();
}
/**
* Open the settings.json location from the standalone no-folders state,
* offered as a fallback next to the Model Paths settings button.
*/
async function handleOpenSettingsFolderClick() {
const button = document.getElementById('openSettingsFolderBtn');
if (!button || button.disabled) return;
button.disabled = true;
try {
const response = await fetch('/api/lm/settings/open-location', { method: 'POST' });
const data = await response.json().catch(() => ({}));
if (!response.ok || data.success === false) {
throw new Error(data.error || `HTTP ${response.status}`);
}
if (data.mode === 'clipboard' && data.path) {
try {
await navigator.clipboard.writeText(data.path);
showToast('settings.openSettingsFileLocation.copied', { path: data.path }, 'success');
} catch (clipboardError) {
console.warn('Clipboard API not available:', clipboardError);
showToast('settings.openSettingsFileLocation.clipboardFallback', { path: data.path }, 'info');
}
} else {
showToast('settings.openSettingsFileLocation.success', {}, 'success');
}
} catch (error) {
console.error('Failed to open settings location:', error);
showToast('settings.openSettingsFileLocation.failed', {}, 'error');
} finally {
button.disabled = false;
}
}
async function initializeWorkflowsDisabledPage() {
// appCore.initialize() wires the shared header (theme, settings modal,
// language) so this page is not a dead end.
await appCore.initialize();
const button = document.getElementById('enableWorkflowsBtn');
if (button) {
button.addEventListener('click', handleEnableClick);
}
const settingsButton = document.getElementById('openWorkflowsSettingsBtn');
if (settingsButton) {
settingsButton.addEventListener('click', handleOpenSettingsClick);
}
const modelPathsButton = document.getElementById('openModelPathsSettingsBtn');
if (modelPathsButton) {
modelPathsButton.addEventListener('click', handleOpenModelPathsSettingsClick);
}
const settingsFolderButton = document.getElementById('openSettingsFolderBtn');
if (settingsFolderButton) {
settingsFolderButton.addEventListener('click', handleOpenSettingsFolderClick);
}
}
document.addEventListener('DOMContentLoaded', initializeWorkflowsDisabledPage);
export { handleEnableClick as enableWorkflows, handleOpenSettingsFolderClick, initializeWorkflowsDisabledPage };