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.
162 lines
6.6 KiB
HTML
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 %}
|