mirror of
https://github.com/mudler/LocalAI.git
synced 2026-03-31 13:15:51 -04:00
fix(video): initialize model selection dropdown with current model value
The Alpine.js link variable was starting empty, causing the dropdown
selection to not reflect the currently selected model. This fix initializes
the link variable with the current model value from the template (e.g.,
video/{{.Model}}), following the same pattern used in image.html.
Signed-off-by: localai-bot <localai-bot@users.noreply.github.com>
Co-authored-by: localai-bot <localai-bot@users.noreply.github.com>
320 lines
20 KiB
HTML
320 lines
20 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
{{template "views/partials/head" .}}
|
||
<script defer src="static/video.js"></script>
|
||
|
||
<body class="bg-[var(--color-bg-primary)] text-[var(--color-text-primary)]">
|
||
<div class="app-layout">
|
||
{{template "views/partials/navbar" .}}
|
||
|
||
<main class="main-content">
|
||
<div class="main-content-inner h-screen flex flex-col">
|
||
<div class="flex flex-1 overflow-hidden">
|
||
<!-- Two Column Layout: Settings on Left, Preview on Right -->
|
||
<div class="flex flex-col lg:flex-row flex-1 gap-4 p-4 overflow-hidden">
|
||
<!-- Left Column: Generation Settings -->
|
||
<div class="flex-shrink-0 lg:w-1/4 flex flex-col min-h-0">
|
||
<div class="card p-3 space-y-3 overflow-y-auto flex-1">
|
||
<!-- Model Selection - Compact -->
|
||
<div class="space-y-1.5">
|
||
<div class="flex items-center justify-between gap-2">
|
||
<label class="text-xs font-medium text-[var(--color-text-secondary)] uppercase tracking-wide flex-shrink-0">Model</label>
|
||
</div>
|
||
<select x-data="{ link : '{{if .Model}}video/{{.Model}}{{end}}' }" x-model="link" x-init="$watch('link', value => window.location = link)"
|
||
id="model-select"
|
||
class="input w-full p-1.5 text-xs"
|
||
>
|
||
<option value="" disabled class="text-[var(--color-text-secondary)]">Select a model</option>
|
||
{{ $model:=.Model}}
|
||
{{ range .ModelsConfig }}
|
||
{{ $cfg := . }}
|
||
{{ range .KnownUsecaseStrings }}
|
||
{{ if eq . "FLAG_VIDEO" }}
|
||
<option value="video/{{$cfg.Name}}" {{ if eq $cfg.Name $model }} selected {{end}} class="bg-[var(--color-bg-primary)] text-[var(--color-text-primary)]">{{$cfg.Name}}</option>
|
||
{{ end }}
|
||
{{ end }}
|
||
{{ end }}
|
||
{{ range .ModelsWithoutConfig }}
|
||
<option value="video/{{.}}" {{ if eq . $model }} selected {{ end }} class="bg-[var(--color-bg-primary)] text-[var(--color-text-primary)]">{{.}}</option>
|
||
{{end}}
|
||
</select>
|
||
</div>
|
||
|
||
<div class="relative">
|
||
<input id="video-model" type="hidden" value="{{.Model}}">
|
||
<form id="genvideo" action="video/{{.Model}}" method="get">
|
||
<!-- Basic Settings -->
|
||
<div class="space-y-2">
|
||
<!-- Prompt -->
|
||
<div class="space-y-1">
|
||
<label for="input" class="block text-xs font-medium text-[var(--color-text-secondary)] uppercase tracking-wide">
|
||
<i class="fas fa-magic mr-1.5 text-[var(--color-primary)]"></i>Prompt
|
||
</label>
|
||
<textarea
|
||
id="input"
|
||
name="input"
|
||
placeholder="Describe the video you want to generate..."
|
||
autocomplete="off"
|
||
rows="3"
|
||
class="input w-full p-1.5 text-xs resize-y"
|
||
required
|
||
></textarea>
|
||
</div>
|
||
|
||
<!-- Negative Prompt -->
|
||
<div class="space-y-1">
|
||
<label for="negative-prompt" class="block text-xs font-medium text-[var(--color-text-secondary)] uppercase tracking-wide">
|
||
<i class="fas fa-ban mr-1.5 text-[var(--color-primary)]"></i>Negative Prompt
|
||
</label>
|
||
<textarea
|
||
id="negative-prompt"
|
||
name="negative-prompt"
|
||
placeholder="Things to avoid in the video..."
|
||
rows="2"
|
||
class="input w-full p-1.5 text-xs resize-y"
|
||
></textarea>
|
||
</div>
|
||
|
||
<!-- Size Selection with Presets -->
|
||
<div class="space-y-1">
|
||
<label for="video-size" class="block text-xs font-medium text-[var(--color-text-secondary)] uppercase tracking-wide">
|
||
<i class="fas fa-expand-arrows-alt mr-1.5 text-[var(--color-primary)]"></i>Video Size
|
||
</label>
|
||
<div class="flex flex-wrap gap-1.5 mb-1.5">
|
||
<button type="button" class="size-preset px-2 py-0.5 text-[10px] rounded border border-[var(--color-border)] hover:bg-[var(--color-bg-secondary)]" data-size="256x256">256×256</button>
|
||
<button type="button" class="size-preset px-2 py-0.5 text-[10px] rounded border border-[var(--color-border)] hover:bg-[var(--color-bg-secondary)]" data-size="512x512">512×512</button>
|
||
<button type="button" class="size-preset px-2 py-0.5 text-[10px] rounded border border-[var(--color-border)] hover:bg-[var(--color-bg-secondary)]" data-size="768x768">768×768</button>
|
||
<button type="button" class="size-preset px-2 py-0.5 text-[10px] rounded border border-[var(--color-border)] hover:bg-[var(--color-bg-secondary)]" data-size="1024x1024">1024×1024</button>
|
||
</div>
|
||
<input
|
||
type="text"
|
||
id="video-size"
|
||
value="512x512"
|
||
placeholder="e.g., 256x256, 512x512, 1024x1024"
|
||
class="input p-1.5 text-xs w-full"
|
||
/>
|
||
</div>
|
||
|
||
<!-- Video Duration / FPS / Frames -->
|
||
<div class="space-y-1">
|
||
<label for="video-seconds" class="block text-xs font-medium text-[var(--color-text-secondary)] uppercase tracking-wide">
|
||
<i class="fas fa-clock mr-1.5 text-[var(--color-primary)]"></i>Duration (seconds)
|
||
</label>
|
||
<input
|
||
type="number"
|
||
id="video-seconds"
|
||
name="seconds"
|
||
min="1"
|
||
max="60"
|
||
placeholder="Leave empty for default"
|
||
class="input p-1.5 text-xs w-full"
|
||
/>
|
||
</div>
|
||
|
||
<div class="space-y-1">
|
||
<label for="video-fps" class="block text-xs font-medium text-[var(--color-text-secondary)] uppercase tracking-wide">
|
||
<i class="fas fa-film mr-1.5 text-[var(--color-primary)]"></i>FPS
|
||
</label>
|
||
<input
|
||
type="number"
|
||
id="video-fps"
|
||
name="fps"
|
||
min="1"
|
||
max="60"
|
||
value="16"
|
||
placeholder="Frames per second"
|
||
class="input p-1.5 text-xs w-full"
|
||
/>
|
||
</div>
|
||
|
||
<div class="space-y-1">
|
||
<label for="video-frames" class="block text-xs font-medium text-[var(--color-text-secondary)] uppercase tracking-wide">
|
||
<i class="fas fa-images mr-1.5 text-[var(--color-primary)]"></i>Number of Frames
|
||
</label>
|
||
<input
|
||
type="number"
|
||
id="video-frames"
|
||
name="num_frames"
|
||
min="1"
|
||
max="500"
|
||
placeholder="Leave empty for default"
|
||
class="input p-1.5 text-xs w-full"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Advanced Settings (Collapsible) -->
|
||
<div class="space-y-2">
|
||
<button type="button" id="advanced-toggle" class="w-full flex items-center justify-between px-2 py-1.5 text-xs rounded text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] hover:bg-[var(--color-bg-secondary)] transition-colors">
|
||
<span><i class="fa-solid fa-sliders mr-1.5 text-[var(--color-primary)]"></i> Advanced Settings</span>
|
||
<i class="fas fa-chevron-down text-[10px]" id="advanced-chevron"></i>
|
||
</button>
|
||
<div id="advanced-settings" class="hidden p-2 bg-[var(--color-bg-secondary)] border border-[var(--color-primary-border)]/20 rounded pl-4 border-l-2 border-[var(--color-bg-secondary)] space-y-2">
|
||
<!-- Steps -->
|
||
<div class="space-y-1">
|
||
<label for="video-steps" class="block text-xs text-[var(--color-text-secondary)]">
|
||
<i class="fas fa-step-forward mr-1.5 text-[var(--color-primary)]"></i>Steps
|
||
</label>
|
||
<input
|
||
type="number"
|
||
id="video-steps"
|
||
name="step"
|
||
min="1"
|
||
max="100"
|
||
placeholder="Leave empty for default"
|
||
class="input p-1.5 text-xs w-full"
|
||
/>
|
||
</div>
|
||
|
||
<!-- Seed -->
|
||
<div class="space-y-1">
|
||
<label for="video-seed" class="block text-xs text-[var(--color-text-secondary)]">
|
||
<i class="fas fa-seedling mr-1.5 text-[var(--color-primary)]"></i>Seed
|
||
</label>
|
||
<input
|
||
type="number"
|
||
id="video-seed"
|
||
name="seed"
|
||
min="0"
|
||
placeholder="Leave empty for random"
|
||
class="input p-1.5 text-xs w-full"
|
||
/>
|
||
</div>
|
||
|
||
<!-- CFG Scale -->
|
||
<div class="space-y-1">
|
||
<label for="video-cfg-scale" class="block text-xs text-[var(--color-text-secondary)]">
|
||
<i class="fas fa-sliders-h mr-1.5 text-[var(--color-primary)]"></i>CFG Scale
|
||
</label>
|
||
<input
|
||
type="number"
|
||
id="video-cfg-scale"
|
||
name="cfg_scale"
|
||
min="0"
|
||
max="20"
|
||
step="0.1"
|
||
placeholder="Leave empty for default"
|
||
class="input p-1.5 text-xs w-full"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Image Inputs (Collapsible) -->
|
||
<div class="space-y-2">
|
||
<button type="button" id="image-inputs-toggle" class="w-full flex items-center justify-between px-2 py-1.5 text-xs rounded text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] hover:bg-[var(--color-bg-secondary)] transition-colors">
|
||
<span><i class="fa-solid fa-image mr-1.5 text-[var(--color-primary)]"></i> Image Inputs</span>
|
||
<i class="fas fa-chevron-down text-[10px]" id="image-inputs-chevron"></i>
|
||
</button>
|
||
<div id="image-inputs-settings" class="hidden p-2 bg-[var(--color-bg-secondary)] border border-[var(--color-primary-border)]/20 rounded pl-4 border-l-2 border-[var(--color-bg-secondary)] space-y-2">
|
||
<!-- Start Image (img2video) -->
|
||
<div class="space-y-1">
|
||
<label for="start-image" class="block text-xs text-[var(--color-text-secondary)]">
|
||
<i class="fas fa-play-circle mr-1.5 text-[var(--color-primary)]"></i>Start Image (img2video)
|
||
</label>
|
||
<input
|
||
type="file"
|
||
id="start-image"
|
||
name="start_image"
|
||
accept="image/*"
|
||
class="input p-1.5 text-xs w-full"
|
||
/>
|
||
</div>
|
||
|
||
<!-- End Image -->
|
||
<div class="space-y-1">
|
||
<label for="end-image" class="block text-xs text-[var(--color-text-secondary)]">
|
||
<i class="fas fa-stop-circle mr-1.5 text-[var(--color-primary)]"></i>End Image
|
||
</label>
|
||
<input
|
||
type="file"
|
||
id="end-image"
|
||
name="end_image"
|
||
accept="image/*"
|
||
class="input p-1.5 text-xs w-full"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Submit Button -->
|
||
<div>
|
||
<button
|
||
type="submit"
|
||
id="generate-btn"
|
||
class="w-full px-2 py-1.5 text-xs rounded text-[var(--color-bg-primary)] bg-[var(--color-primary)] hover:bg-[var(--color-primary)]/90 transition-colors font-medium"
|
||
>
|
||
<i class="fas fa-video mr-1.5"></i>Generate Video
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Right Column: Video Preview -->
|
||
<div class="flex-grow lg:w-3/4 flex flex-col min-h-0">
|
||
<div class="relative flex-1 min-h-0 overflow-y-auto">
|
||
<!-- Loading Animation -->
|
||
<div id="loader" class="hidden absolute inset-0 flex items-center justify-center bg-[var(--color-bg-primary)]/80 rounded-xl z-10">
|
||
<div class="text-center">
|
||
<svg class="animate-spin h-10 w-10 text-[var(--color-primary)] mx-auto mb-3" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||
</svg>
|
||
<p class="text-xs text-[var(--color-text-secondary)]">Generating video...</p>
|
||
</div>
|
||
</div>
|
||
<!-- Placeholder when no videos -->
|
||
<div id="result-placeholder" class="min-h-[400px] flex items-center justify-center flex-shrink-0">
|
||
<p class="text-xs text-[var(--color-text-secondary)] italic text-center">Your generated videos will appear here</p>
|
||
</div>
|
||
<!-- Results container -->
|
||
<div id="result" class="grid grid-cols-1 gap-4 pb-4"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</main>
|
||
</div>
|
||
|
||
<script>
|
||
// Collapsible sections
|
||
document.getElementById('advanced-toggle').addEventListener('click', function() {
|
||
const settings = document.getElementById('advanced-settings');
|
||
const chevron = document.getElementById('advanced-chevron');
|
||
settings.classList.toggle('hidden');
|
||
chevron.classList.toggle('fa-chevron-down');
|
||
chevron.classList.toggle('fa-chevron-up');
|
||
});
|
||
|
||
document.getElementById('image-inputs-toggle').addEventListener('click', function() {
|
||
const settings = document.getElementById('image-inputs-settings');
|
||
const chevron = document.getElementById('image-inputs-chevron');
|
||
settings.classList.toggle('hidden');
|
||
chevron.classList.toggle('fa-chevron-down');
|
||
chevron.classList.toggle('fa-chevron-up');
|
||
});
|
||
|
||
// Size preset buttons
|
||
document.querySelectorAll('.size-preset').forEach(button => {
|
||
button.addEventListener('click', function() {
|
||
const size = this.getAttribute('data-size');
|
||
document.getElementById('video-size').value = size;
|
||
// Update active state
|
||
document.querySelectorAll('.size-preset').forEach(btn => {
|
||
btn.classList.remove('bg-[var(--color-primary)]', 'text-white');
|
||
});
|
||
this.classList.add('bg-[var(--color-primary)]', 'text-white');
|
||
});
|
||
});
|
||
|
||
// Set initial active size preset
|
||
document.querySelector('.size-preset[data-size="512x512"]').classList.add('bg-[var(--color-primary)]', 'text-white');
|
||
</script>
|
||
|
||
</body>
|
||
</html>
|