Adding files.
This commit is contained in:
298
templates/run_details.html
Normal file
298
templates/run_details.html
Normal file
@@ -0,0 +1,298 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h2><i class="fas fa-book me-2"></i>Run #{{ run.id }}</h2>
|
||||
<p class="text-muted mb-0">Project: <a href="{{ url_for('view_project', id=run.project_id) }}">{{ run.project.name }}</a></p>
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn btn-outline-primary me-2" type="button" data-bs-toggle="collapse" data-bs-target="#bibleCollapse" aria-expanded="false" aria-controls="bibleCollapse">
|
||||
<i class="fas fa-scroll me-2"></i>Show Bible
|
||||
</button>
|
||||
<a href="{{ url_for('view_project', id=run.project_id) }}" class="btn btn-outline-secondary">Back to Project</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Collapsible Bible Viewer -->
|
||||
<div class="collapse mb-4" id="bibleCollapse">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-light">
|
||||
<h5 class="mb-0"><i class="fas fa-book-open me-2"></i>Project Bible</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if bible %}
|
||||
<ul class="nav nav-tabs" id="bibleTabs" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" id="meta-tab" data-bs-toggle="tab" data-bs-target="#meta" type="button" role="tab">Metadata</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="chars-tab" data-bs-toggle="tab" data-bs-target="#chars" type="button" role="tab">Characters</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="plot-tab" data-bs-toggle="tab" data-bs-target="#plot" type="button" role="tab">Plot</button>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content p-3 border border-top-0">
|
||||
<!-- Metadata Tab -->
|
||||
<div class="tab-pane fade show active" id="meta" role="tabpanel">
|
||||
<dl class="row mb-0">
|
||||
{% for k, v in bible.project_metadata.items() %}
|
||||
{% if k not in ['style', 'length_settings', 'author_details'] %}
|
||||
<dt class="col-sm-3 text-capitalize">{{ k.replace('_', ' ') }}</dt>
|
||||
<dd class="col-sm-9">{{ v }}</dd>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if bible.project_metadata.style %}
|
||||
<dt class="col-sm-12 mt-3 border-bottom pb-2">Style Settings</dt>
|
||||
{% for k, v in bible.project_metadata.style.items() %}
|
||||
<dt class="col-sm-3 text-capitalize mt-2">{{ k.replace('_', ' ') }}</dt>
|
||||
<dd class="col-sm-9 mt-2">{{ v|join(', ') if v is sequence and v is not string else v }}</dd>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<!-- Characters Tab -->
|
||||
<div class="tab-pane fade" id="chars" role="tabpanel">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-hover align-middle">
|
||||
<thead class="table-light"><tr><th>Name</th><th>Role</th><th>Description</th></tr></thead>
|
||||
<tbody>
|
||||
{% for c in bible.characters %}
|
||||
<tr>
|
||||
<td class="fw-bold">{{ c.name }}</td>
|
||||
<td><span class="badge bg-secondary">{{ c.role }}</span></td>
|
||||
<td class="small">{{ c.description }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Plot Tab -->
|
||||
<div class="tab-pane fade" id="plot" role="tabpanel">
|
||||
{% for book in bible.books %}
|
||||
<div class="mb-3">
|
||||
<h6 class="fw-bold text-primary">Book {{ book.book_number }}: {{ book.title }}</h6>
|
||||
<p class="fst-italic small text-muted">{{ book.manual_instruction }}</p>
|
||||
<ol class="list-group list-group-numbered">
|
||||
{% for beat in book.plot_beats %}
|
||||
<li class="list-group-item list-group-item-action border-0 py-1">{{ beat }}</li>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-warning">Bible data not found for this project.</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Status Bar -->
|
||||
<div class="card shadow-sm mb-4">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between mb-2">
|
||||
<span class="fw-bold" id="status-text">Status: {{ run.status|title }}</span>
|
||||
<span class="text-muted" id="run-duration">{{ run.duration() }}</span>
|
||||
</div>
|
||||
<div class="progress" style="height: 20px;">
|
||||
<div id="status-bar" class="progress-bar {% if run.status == 'running' %}progress-bar-striped progress-bar-animated{% elif run.status == 'failed' %}bg-danger{% else %}bg-success{% endif %}"
|
||||
role="progressbar" style="width: {% if run.status == 'completed' %}100%{% elif run.status == 'running' %}100%{% else %}5%{% endif %}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- Left Column: Cover Art -->
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="card shadow-sm h-100">
|
||||
<div class="card-header bg-light">
|
||||
<h5 class="mb-0"><i class="fas fa-image me-2"></i>Cover Art</h5>
|
||||
</div>
|
||||
<div class="card-body text-center">
|
||||
{% if has_cover %}
|
||||
<img src="{{ url_for('download_artifact', run_id=run.id, file='cover.png') }}" class="img-fluid rounded shadow-sm mb-3" alt="Book Cover">
|
||||
{% else %}
|
||||
<div class="alert alert-secondary py-5">
|
||||
<i class="fas fa-image fa-3x mb-3"></i><br>
|
||||
No cover generated yet.
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<hr>
|
||||
|
||||
<form action="{{ url_for('regenerate_artifacts', run_id=run.id) }}" method="POST">
|
||||
<label class="form-label text-start w-100 small fw-bold">Regenerate Art & Files</label>
|
||||
<textarea name="feedback" class="form-control mb-2" rows="2" placeholder="Feedback (e.g. 'Make the font larger', 'Use a darker theme')..."></textarea>
|
||||
<button type="submit" class="btn btn-primary w-100">
|
||||
<i class="fas fa-sync me-2"></i>Regenerate
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right Column: Blurb & Stats -->
|
||||
<div class="col-md-8 mb-4">
|
||||
<!-- Blurb -->
|
||||
<div class="card shadow-sm mb-4">
|
||||
<div class="card-header bg-light">
|
||||
<h5 class="mb-0"><i class="fas fa-align-left me-2"></i>Blurb</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if blurb %}
|
||||
<p class="card-text" style="white-space: pre-wrap;">{{ blurb }}</p>
|
||||
{% else %}
|
||||
<p class="text-muted fst-italic">Blurb not generated yet.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Stats -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-6">
|
||||
<div class="card shadow-sm text-center">
|
||||
<div class="card-body">
|
||||
<h6 class="text-muted">Total Cost</h6>
|
||||
<h3 class="text-success" id="run-cost">${{ "%.4f"|format(run.cost) }}</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="card shadow-sm text-center">
|
||||
<div class="card-body">
|
||||
<h6 class="text-muted">Artifacts</h6>
|
||||
<h3>
|
||||
{% if has_cover %}<i class="fas fa-check text-success me-2"></i>{% endif %}
|
||||
{% if blurb %}<i class="fas fa-check text-success"></i>{% endif %}
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tracking & Warnings -->
|
||||
{% if tracking and (tracking.content_warnings or tracking.characters) %}
|
||||
<div class="card shadow-sm mb-4 border-warning">
|
||||
<div class="card-header bg-warning-subtle">
|
||||
<h5 class="mb-0 text-warning-emphasis"><i class="fas fa-exclamation-triangle me-2"></i>Story Tracking & Warnings</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if tracking.content_warnings %}
|
||||
<div class="mb-3">
|
||||
<h6 class="fw-bold">Content Warnings Detected:</h6>
|
||||
<div>
|
||||
{% for w in tracking.content_warnings %}
|
||||
<span class="badge bg-danger me-1 mb-1">{{ w }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if tracking.characters %}
|
||||
{# Check if any character actually has major events to display #}
|
||||
{% set has_events = namespace(value=false) %}
|
||||
{% for name, data in tracking.characters.items() %}
|
||||
{% if data.major_events %}{% set has_events.value = true %}{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if has_events.value %}
|
||||
<h6 class="fw-bold">Major Character Events:</h6>
|
||||
<div class="accordion" id="charEventsAcc">
|
||||
{% for name, data in tracking.characters.items() %}
|
||||
{% if data.major_events %}
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed py-2" type="button" data-bs-toggle="collapse" data-bs-target="#ce-{{ loop.index }}">
|
||||
{{ name }}
|
||||
</button>
|
||||
</h2>
|
||||
<div id="ce-{{ loop.index }}" class="accordion-collapse collapse" data-bs-parent="#charEventsAcc">
|
||||
<div class="accordion-body small py-2">
|
||||
<ul class="mb-0 ps-3">
|
||||
{% for evt in data.major_events %}
|
||||
<li>{{ evt }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Collapsible Log -->
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-dark text-white d-flex justify-content-between align-items-center" style="cursor: pointer;" data-bs-toggle="collapse" data-bs-target="#logCollapse">
|
||||
<h5 class="mb-0"><i class="fas fa-terminal me-2"></i>System Log</h5>
|
||||
<span class="badge bg-secondary" id="log-badge">Click to Toggle</span>
|
||||
</div>
|
||||
<div id="logCollapse" class="collapse {% if run.status == 'running' %}show{% endif %}">
|
||||
<div class="card-body bg-dark p-0">
|
||||
<pre id="console-log" class="console-log m-0 p-3" style="color: #00ff00; background-color: #1e1e1e; height: 400px; overflow-y: auto; font-family: monospace; font-size: 0.85rem;">{{ log_content or "Waiting for logs..." }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const runId = {{ run.id }};
|
||||
const consoleEl = document.getElementById('console-log');
|
||||
const statusText = document.getElementById('status-text');
|
||||
const statusBar = document.getElementById('status-bar');
|
||||
const costEl = document.getElementById('run-cost');
|
||||
|
||||
function updateLog() {
|
||||
fetch(`/run/${runId}/status`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
// Update Status Text
|
||||
statusText.innerText = "Status: " + data.status.charAt(0).toUpperCase() + data.status.slice(1);
|
||||
costEl.innerText = '$' + parseFloat(data.cost).toFixed(4);
|
||||
|
||||
// Update Status Bar
|
||||
if (data.status === 'running' || data.status === 'queued') {
|
||||
statusBar.className = "progress-bar progress-bar-striped progress-bar-animated";
|
||||
statusBar.style.width = "100%";
|
||||
} else if (data.status === 'failed') {
|
||||
statusBar.className = "progress-bar bg-danger";
|
||||
statusBar.style.width = "100%";
|
||||
} else {
|
||||
statusBar.className = "progress-bar bg-success";
|
||||
statusBar.style.width = "100%";
|
||||
}
|
||||
|
||||
// Update Log (only if changed to avoid scroll jitter)
|
||||
if (consoleEl.innerText !== data.log) {
|
||||
const isScrolledToBottom = consoleEl.scrollHeight - consoleEl.clientHeight <= consoleEl.scrollTop + 50;
|
||||
consoleEl.innerText = data.log;
|
||||
if (isScrolledToBottom) {
|
||||
consoleEl.scrollTop = consoleEl.scrollHeight;
|
||||
}
|
||||
}
|
||||
|
||||
// Poll if running
|
||||
if (data.status === 'running' || data.status === 'queued') {
|
||||
setTimeout(updateLog, 2000);
|
||||
}
|
||||
})
|
||||
.catch(err => console.error(err));
|
||||
}
|
||||
|
||||
// Start polling
|
||||
updateLog();
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user