Files
willmiao-ComfyUI-Lora-Manager/templates/workflows.html
T
Will Miao f473678004 feat(workflows): restore a workflow to its downloaded state
New card context menu entry 'Restore Downloaded Version' discards local
edits made via the write-back flow:

- Container artifacts (.zip/images) were never modified — restore deletes
  the derived .workflow.json sidecar and re-extracts from the pristine
  archive, then re-syncs analysis metadata. Purely local, works for
  self-hosted workflows too.
- Bare .json artifacts are rewritten in place by write-backs, so restore
  re-downloads the recorded CivitAI version file (API-key auth, temp file
  + workflow validation + SHA256 check before the atomic replace).

The menu entry is disabled for local-only .json workflows that have no
restore source. On success the card is refreshed in place with the
shared 'Updated' cue.
2026-10-29 12:12:52 +08:00

162 lines
6.6 KiB
HTML

{% extends "base.html" %}
{% block title %}{{ t('workflows.title') }}{% endblock %}
{% block page_id %}workflows{% endblock %}
{% block init_title %}{{ t('initialization.workflows.title') }}{% endblock %}
{% block init_message %}{{ t('initialization.workflows.message') }}{% endblock %}
{% block init_check_url %}/api/lm/workflows/list?page=1&page_size=1{% endblock %}
{% block page_css %}
<style>
.workflows-disabled {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 12px;
text-align: center;
padding: 64px 24px;
color: var(--text-color, #e0e0e0);
}
.workflows-disabled > i {
font-size: 48px;
opacity: 0.6;
}
.workflows-disabled h2 {
margin: 0;
font-size: 20px;
}
.workflows-disabled p {
margin: 0;
max-width: 520px;
line-height: 1.6;
opacity: 0.8;
}
.workflows-disabled .workflows-disabled-hint {
font-size: 13px;
opacity: 0.6;
}
.workflows-disabled button {
margin-top: 8px;
padding: 10px 20px;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
color: #fff;
background: var(--primary-color, #4a7dff);
}
.workflows-disabled button:disabled {
opacity: 0.6;
cursor: default;
}
.workflows-settings-file {
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
}
.workflows-settings-file code {
padding: 4px 8px;
border-radius: 4px;
background: rgba(127, 127, 127, 0.15);
border: 1px solid rgba(127, 127, 127, 0.25);
word-break: break-all;
}
</style>
{% endblock %}
{% block additional_components %}
<div id="workflowContextMenu" class="context-menu" style="display: none;">
<!-- Open -->
<div class="context-menu-item" data-action="send-to-comfy"><i class="fas fa-paper-plane"></i> {{ t('workflows.contextMenu.sendToComfy') }}</div>
<div class="context-menu-item disabled" data-action="update-from-comfy"><i class="fas fa-download"></i> {{ t('workflows.contextMenu.updateFromComfy') }}</div>
<div class="context-menu-item" data-action="copy-workflow"><i class="fas fa-clipboard"></i> {{ t('workflows.contextMenu.copyWorkflow') }}</div>
<div class="context-menu-item" data-action="restore-download"><i class="fas fa-undo"></i> {{ t('workflows.contextMenu.restoreDownloaded') }}</div>
<div class="context-menu-separator menu-section-break"></div>
<!-- Metadata -->
<div class="context-menu-item" data-action="refresh-metadata"><i class="fas fa-sync"></i> {{ t('loras.contextMenu.refreshMetadata') }}</div>
<div class="context-menu-item" data-action="relink-civitai"><i class="fas fa-external-link-alt"></i> {{ t('loras.contextMenu.linkCivitai') }}</div>
<div class="context-menu-separator menu-section-break"></div>
<!-- Workflow -->
<div class="context-menu-item" data-action="copyname"><i class="fas fa-copy"></i> {{ t('loras.contextMenu.copyFilename') }}</div>
<div class="context-menu-separator menu-section-break"></div>
<!-- Media / Preview -->
<div class="context-menu-item" data-action="replace-preview"><i class="fas fa-image"></i> {{ t('loras.contextMenu.replacePreview') }}</div>
<div class="context-menu-separator menu-section-break"></div>
<!-- Attributes -->
<div class="context-menu-item" data-action="set-nsfw"><i class="fas fa-exclamation-triangle"></i> {{ t('loras.contextMenu.setContentRating') }}</div>
<div class="context-menu-separator menu-section-break"></div>
<!-- Organization -->
<div class="context-menu-item" data-action="move"><i class="fas fa-folder-open"></i> {{ t('loras.contextMenu.moveToFolder') }}</div>
<div class="context-menu-separator"></div>
<!-- Destructive -->
<div class="context-menu-item" data-action="exclude"><i class="fas fa-eye-slash"></i> {{ t('loras.contextMenu.excludeModel') }}</div>
<div class="context-menu-item delete-item" data-action="delete"><i class="fas fa-trash"></i> {{ t('loras.contextMenu.deleteModel') }}</div>
</div>
{% endblock %}
{% block content %}
{% if workflows_disabled %}
<div class="workflows-disabled">
<i class="fas fa-diagram-project"></i>
<h2>{{ t('workflows.disabled.title') }}</h2>
<p>{{ t('workflows.disabled.description') }}</p>
<button id="enableWorkflowsBtn" type="button">
<i class="fas fa-toggle-on"></i> {{ t('workflows.disabled.enableButton') }}
</button>
<p class="workflows-disabled-hint">{{ t('workflows.disabled.hint') }}</p>
</div>
{% elif workflows_no_paths %}
<div class="workflows-disabled">
<i class="fas fa-folder-open"></i>
<h2>{{ t('workflows.noPaths.title') }}</h2>
{% if standalone_mode %}
<p>{{ t('workflows.noPaths.descriptionStandalone') }}</p>
<p class="workflows-disabled-hint">{{ t('workflows.noPaths.hintStandalone') }}</p>
<button id="openModelPathsSettingsBtn" type="button">
<i class="fas fa-cog"></i> {{ t('workflows.noPaths.openModelPaths') }}
</button>
{% if settings_file %}
<p class="workflows-settings-file"><i class="fas fa-file-alt"></i> <code>{{ settings_file }}</code></p>
{% endif %}
<button id="openSettingsFolderBtn" type="button">
<i class="fas fa-folder-open"></i> {{ t('workflows.noPaths.openSettingsFolder') }}
</button>
{% else %}
<p>{{ t('workflows.noPaths.descriptionComfyUI') }}</p>
<p class="workflows-disabled-hint">{{ t('workflows.noPaths.hintComfyUI') }}</p>
<button id="openWorkflowsSettingsBtn" type="button">
<i class="fas fa-cog"></i> {{ t('workflows.noPaths.openSettings') }}
</button>
{% endif %}
</div>
{% else %}
<div class="sticky-topbar">
{% include 'components/controls.html' %}
{% include 'components/breadcrumb.html' %}
</div>
{% include 'components/duplicates_banner.html' %}
{% include 'components/folder_sidebar.html' %}
<!-- Workflow cards container -->
<div class="card-grid" id="modelGrid">
<!-- Cards will be dynamically inserted here -->
</div>
{% endif %}
{% endblock %}
{% block overlay %}
<div class="bulk-mode-overlay"></div>
{% endblock %}
{% block main_script %}
{% if workflows_disabled or workflows_no_paths %}
<script type="module" src="{{ rel_prefix }}loras_static/js/workflows_disabled.js?v={{ version }}"></script>
{% else %}
<script type="module" src="{{ rel_prefix }}loras_static/js/workflows.js?v={{ version }}"></script>
{% endif %}
{% endblock %}