Two initialization-page defects made failures look like progress: - A cache read exception while rendering a model/recipe page forced is_initializing=True, showing an endless spinner; worse, the polling fallback then reported the idle scanner as "complete", reloading the page straight back into the same exception. Page handlers now pass init_error to the initialization template, which renders the error state (with retry) server-side, and initialization.js stays on it instead of starting WebSocket/polling. - The WebSocket path reloaded once the page's own scanner finished, while the polling fallback waited for every scanner. The polling fallback now mirrors the per-page semantics: /api/lm/init-status accepts ?page=<pageType> (aggregate behavior preserved without it), and ModelScanner records _init_error so a failed initial scan is reported as "error" instead of masquerading as "complete". Polling stops on error instead of reloading into the failure.
102 lines
5.5 KiB
HTML
102 lines
5.5 KiB
HTML
<!-- Initialization Component -->
|
|
<div class="initialization-container" id="initializationContainer">
|
|
<div class="initialization-content">
|
|
<div class="initialization-header">
|
|
<h2 id="initTitle">{% block init_title %}{{ t('initialization.title') }}{% endblock %}</h2>
|
|
<p class="init-subtitle" id="initSubtitle">{% block init_message %}{{ t('initialization.message') }}{% endblock %}
|
|
</p>
|
|
</div>
|
|
|
|
<div class="loading-content"{% if init_error %} hidden{% endif %}>
|
|
<div class="loading-spinner"></div>
|
|
<div class="loading-status" id="progressStatus">{{ t('initialization.status') }}</div>
|
|
<!-- Use initialization-specific classes for the progress bar -->
|
|
<div class="init-progress-container">
|
|
<div class="init-progress-bar" id="initProgressBar"></div>
|
|
</div>
|
|
<div class="progress-details">
|
|
<span id="progressPercentage">0%</span>
|
|
<span id="remainingTime">{{ t('initialization.estimatingTime') }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Server-rendered when the page itself failed to load the cache:
|
|
stays visible and initialization.js skips polling for it. -->
|
|
<div class="init-error" id="initError"{% if not init_error %} hidden{% endif %}>
|
|
<i class="fas fa-exclamation-circle"></i>
|
|
<div class="init-error-content">
|
|
<h3>{{ t('initialization.error.title') }}</h3>
|
|
<p id="initErrorText">{{ init_error or '' }}</p>
|
|
<button type="button" class="primary-btn" id="initRetryBtn">{{ t('initialization.error.retry') }}</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="tips-container">
|
|
<div class="tips-header">
|
|
<i class="fas fa-lightbulb"></i>
|
|
<h3>{{ t('initialization.tips.title') }}</h3>
|
|
</div>
|
|
<div class="tips-content">
|
|
<div class="tip-carousel" id="tipCarousel">
|
|
<div class="tip-item active">
|
|
<div class="tip-image">
|
|
<img src="{{ rel_prefix }}loras_static/images/tips/civitai-api.png" alt="{{ t('initialization.tips.civitai.alt') }}"
|
|
onerror="this.src='/loras_static/images/no-preview.png'">
|
|
</div>
|
|
<div class="tip-text">
|
|
<h4>{{ t('initialization.tips.civitai.title') }}</h4>
|
|
<p>{{ t('initialization.tips.civitai.description') }}</p>
|
|
</div>
|
|
</div>
|
|
<div class="tip-item">
|
|
<div class="tip-image">
|
|
<img src="{{ rel_prefix }}loras_static/images/tips/civitai-download.png" alt="{{ t('initialization.tips.download.alt') }}"
|
|
onerror="this.src='/loras_static/images/no-preview.png'">
|
|
</div>
|
|
<div class="tip-text">
|
|
<h4>{{ t('initialization.tips.download.title') }}</h4>
|
|
<p>{{ t('initialization.tips.download.description') }}</p>
|
|
</div>
|
|
</div>
|
|
<div class="tip-item">
|
|
<div class="tip-image">
|
|
<img src="{{ rel_prefix }}loras_static/images/tips/recipes.png" alt="{{ t('initialization.tips.recipes.alt') }}"
|
|
onerror="this.src='/loras_static/images/no-preview.png'">
|
|
</div>
|
|
<div class="tip-text">
|
|
<h4>{{ t('initialization.tips.recipes.title') }}</h4>
|
|
<p>{{ t('initialization.tips.recipes.description') }}</p>
|
|
</div>
|
|
</div>
|
|
<div class="tip-item">
|
|
<div class="tip-image">
|
|
<img src="{{ rel_prefix }}loras_static/images/tips/filter.png" alt="{{ t('initialization.tips.filter.alt') }}"
|
|
onerror="this.src='/loras_static/images/no-preview.png'">
|
|
</div>
|
|
<div class="tip-text">
|
|
<h4>{{ t('initialization.tips.filter.title') }}</h4>
|
|
<p>{{ t('initialization.tips.filter.description') }}</p>
|
|
</div>
|
|
</div>
|
|
<div class="tip-item">
|
|
<div class="tip-image">
|
|
<img src="{{ rel_prefix }}loras_static/images/tips/search.webp" alt="{{ t('initialization.tips.search.alt') }}"
|
|
onerror="this.src='/loras_static/images/no-preview.png'">
|
|
</div>
|
|
<div class="tip-text">
|
|
<h4>{{ t('initialization.tips.search.title') }}</h4>
|
|
<p>{{ t('initialization.tips.search.description') }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="tip-navigation">
|
|
<span class="tip-dot active" data-index="0"></span>
|
|
<span class="tip-dot" data-index="1"></span>
|
|
<span class="tip-dot" data-index="2"></span>
|
|
<span class="tip-dot" data-index="3"></span>
|
|
<span class="tip-dot" data-index="4"></span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div> |