Add extra prompts, endless generation, random character default, and small fixes

- Add extra positive/negative prompt textareas to all 9 detail pages with session persistence
- Add Endless generation button to all detail pages (continuous preview generation until stopped)
- Default character selector to "Random Character" on all secondary detail pages
- Fix queue clear endpoint (remove spurious auth check)
- Refactor app.py into routes/ and services/ modules
- Update CLAUDE.md with new architecture documentation
- Various data file updates and cleanup

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Aodhan Collins
2026-03-13 02:07:16 +00:00
parent 1b8a798c31
commit 5e4348ebc1
170 changed files with 17367 additions and 9781 deletions

View File

@@ -29,17 +29,6 @@
</div>
</div>
<!-- Image Modal -->
<div class="modal fade" id="imageModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-xl modal-dialog-centered">
<div class="modal-content bg-transparent border-0">
<div class="modal-body p-0 text-center">
<img id="modalImage" src="" alt="Enlarged Image" class="img-fluid" style="max-height: 90vh;">
</div>
</div>
</div>
</div>
<div class="d-flex justify-content-between align-items-center mb-3">
<div>
<a href="{{ url_for('presets_index') }}" class="btn btn-sm btn-outline-secondary me-2">Back to Library</a>
@@ -59,8 +48,7 @@
<div class="col-md-4">
<div class="card mb-3">
<div class="img-container" style="height:auto;min-height:400px;cursor:pointer;"
data-bs-toggle="modal" data-bs-target="#imageModal"
onclick="showImage(this.querySelector('img') ? this.querySelector('img').src : '')">
onclick="openGallery([this.querySelector('img') ? this.querySelector('img').src : this.src || ''], 0)">
{% if preset.image_path %}
<img src="{{ url_for('static', filename='uploads/' + preset.image_path) }}"
alt="{{ preset.name }}" class="img-fluid"
@@ -72,18 +60,28 @@
{% endif %}
</div>
<div class="card-body">
<form action="{{ url_for('upload_preset_image', slug=preset.slug) }}" method="post" enctype="multipart/form-data" class="mb-3">
<label class="form-label text-muted small">Update Cover Image</label>
<div class="input-group input-group-sm">
<input class="form-control" type="file" name="image" required>
<button type="submit" class="btn btn-outline-primary">Upload</button>
</div>
</form>
<form id="generate-form" action="{{ url_for('generate_preset_image', slug=preset.slug) }}" method="post">
{# Additional Prompts #}
<div class="mb-2">
<label for="extra_positive" class="form-label">Additional Positive</label>
<textarea class="form-control form-control-sm font-monospace" id="extra_positive" name="extra_positive" rows="2" placeholder="e.g. masterpiece, best quality">{{ extra_positive or '' }}</textarea>
</div>
<div class="mb-3">
<label for="extra_negative" class="form-label">Additional Negative</label>
<textarea class="form-control form-control-sm font-monospace" id="extra_negative" name="extra_negative" rows="2" placeholder="e.g. blurry, low quality">{{ extra_negative or '' }}</textarea>
</div>
<div class="d-grid gap-2">
<button type="submit" name="action" value="preview" class="btn btn-success">Generate Preview</button>
<button type="submit" name="action" value="replace" class="btn btn-outline-warning btn-sm">Generate &amp; Set Cover</button>
<div class="input-group input-group-sm mb-1">
<span class="input-group-text">Seed</span>
<input type="number" class="form-control" id="seed-input" name="seed" placeholder="Random" min="1" step="1">
<button type="button" class="btn btn-outline-secondary" id="seed-clear-btn" title="Clear (random)">&times;</button>
</div>
<button type="submit" name="action" value="preview" class="btn btn-success" data-requires="comfyui">Generate Preview</button>
<button type="button" id="endless-btn" class="btn btn-outline-success" onclick="window._endlessStart()" data-requires="comfyui">Endless</button>
<button type="button" id="endless-stop-btn" class="btn btn-danger d-none" onclick="window._endlessStop()">Stop</button>
<small id="endless-counter" class="text-muted d-none"></small>
<button type="submit" name="action" value="replace" class="btn btn-outline-warning btn-sm" data-requires="comfyui">Generate &amp; Set Cover</button>
</div>
</form>
</div>
@@ -280,7 +278,7 @@ document.getElementById('generate-form').addEventListener('submit', function(e)
btn.disabled = true;
btn.textContent = 'Generating...';
fetch(this.action, {
fetch(this.getAttribute('action'), {
method: 'POST',
headers: {'X-Requested-With': 'XMLHttpRequest'},
body: formData
@@ -319,6 +317,7 @@ function pollJob(jobId, btn, actionVal) {
document.getElementById('generated-images').prepend(col);
document.getElementById('no-images-msg')?.classList.add('d-none');
selectPreview(data.result.relative_path, data.result.image_url);
updateSeedFromResult(data.result);
} else if (data.status === 'failed') {
btn.disabled = false;
btn.textContent = 'Generate Preview';
@@ -346,6 +345,23 @@ document.addEventListener('click', function(e) {
if (img) selectPreview(img.dataset.previewPath, img.src);
});
window._onEndlessResult = function(jobResult) {
if (jobResult.result?.image_url) {
selectPreview(jobResult.result.relative_path, jobResult.result.image_url);
const img = document.createElement('img');
img.src = jobResult.result.image_url;
img.className = 'img-fluid rounded';
img.style.cursor = 'pointer';
img.dataset.previewPath = jobResult.result.relative_path;
img.addEventListener('click', () => selectPreview(jobResult.result.relative_path, img.src));
const col = document.createElement('div');
col.className = 'col-4 col-md-3';
col.appendChild(img);
document.getElementById('generated-images').prepend(col);
document.getElementById('no-images-msg')?.classList.add('d-none');
}
};
// JSON editor
initJsonEditor("{{ url_for('save_preset_json', slug=preset.slug) }}");
</script>