Final changes and update
This commit is contained in:
207
templates/read_book.html
Normal file
207
templates/read_book.html
Normal file
@@ -0,0 +1,207 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="d-flex justify-content-between align-items-center mb-4 sticky-top bg-white py-3 border-bottom" style="z-index: 100;">
|
||||
<div>
|
||||
<h3 class="mb-0"><i class="fas fa-book-reader me-2"></i>{{ book_folder }}</h3>
|
||||
<small class="text-muted">Run #{{ run.id }}</small>
|
||||
</div>
|
||||
<div>
|
||||
<form action="{{ url_for('sync_book_metadata', run_id=run.id, book_folder=book_folder) }}" method="POST" class="d-inline me-2" onsubmit="return confirm('This will re-scan your manuscript to update the character list and author persona. Continue?');">
|
||||
<button type="submit" class="btn btn-outline-info" data-bs-toggle="tooltip" title="Scans your manual edits to update the character database and author writing style. Use this after making significant edits.">
|
||||
<i class="fas fa-sync me-2"></i>Sync Metadata
|
||||
</button>
|
||||
</form>
|
||||
<a href="{{ url_for('view_run', id=run.id) }}" class="btn btn-outline-secondary">Back to Run</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-8">
|
||||
{% for ch in manuscript %}
|
||||
<div class="card shadow-sm mb-5" id="ch-{{ ch.num }}">
|
||||
<div class="card-header bg-light d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">Chapter {{ ch.num }}: {{ ch.title }}</h5>
|
||||
<div>
|
||||
<button class="btn btn-sm btn-outline-warning me-1" data-bs-toggle="modal" data-bs-target="#rewriteModal{{ ch.num|string|replace(' ', '') }}" data-bs-toggle="tooltip" title="Ask AI to rewrite this chapter based on new instructions.">
|
||||
<i class="fas fa-magic"></i> Rewrite
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-primary" onclick="toggleEdit('{{ ch.num }}')">
|
||||
<i class="fas fa-edit"></i> Edit
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- View Mode -->
|
||||
<div class="card-body chapter-content" id="view-{{ ch.num }}">
|
||||
<div class="prose" style="font-family: 'Georgia', serif; font-size: 1.1rem; line-height: 1.6; color: #333;">
|
||||
{{ ch.html_content|safe }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Edit Mode -->
|
||||
<div class="card-body d-none" id="edit-{{ ch.num }}">
|
||||
<textarea class="form-control font-monospace" id="text-{{ ch.num }}" rows="20">{{ ch.content }}</textarea>
|
||||
<div class="d-flex justify-content-end mt-2">
|
||||
<button class="btn btn-secondary me-2" onclick="toggleEdit('{{ ch.num }}')">Cancel</button>
|
||||
<button class="btn btn-success" onclick="saveChapter('{{ ch.num }}')">Save Changes</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Rewrite Modal -->
|
||||
<div class="modal fade" id="rewriteModal{{ ch.num|string|replace(' ', '') }}" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Rewrite Chapter {{ ch.num }} with AI</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Instructions</label>
|
||||
<textarea name="instruction" class="form-control" rows="4" placeholder="e.g. 'Change the setting to a train station', 'Make the protagonist refuse the offer', 'Fix the pacing in the middle section'." required></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn btn-warning" onclick="startRewrite('{{ ch.num }}')">Rewrite Chapter</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<!-- Table of Contents Sidebar -->
|
||||
<div class="col-lg-3 d-none d-lg-block">
|
||||
<div class="sticky-top" style="top: 100px;">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header">Table of Contents</div>
|
||||
<div class="list-group list-group-flush" style="max-height: 70vh; overflow-y: auto;">
|
||||
{% for ch in manuscript %}
|
||||
<a href="#ch-{{ ch.num }}" class="list-group-item list-group-item-action small">
|
||||
{{ ch.num }}. {{ ch.title }}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Progress Modal -->
|
||||
<div class="modal fade" id="progressModal" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body text-center p-4">
|
||||
<div class="spinner-border text-primary mb-3" style="width: 3rem; height: 3rem;"></div>
|
||||
<h4>Processing AI Request...</h4>
|
||||
<p class="text-muted mb-0">This may take a few minutes, especially if subsequent chapters need updates. Please wait.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
let rewritePollInterval = null;
|
||||
|
||||
function toggleEdit(num) {
|
||||
const viewDiv = document.getElementById(`view-${num}`);
|
||||
const editDiv = document.getElementById(`edit-${num}`);
|
||||
|
||||
if (editDiv.classList.contains('d-none')) {
|
||||
editDiv.classList.remove('d-none');
|
||||
viewDiv.classList.add('d-none');
|
||||
} else {
|
||||
editDiv.classList.add('d-none');
|
||||
viewDiv.classList.remove('d-none');
|
||||
}
|
||||
}
|
||||
|
||||
function startRewrite(num) {
|
||||
const modal = document.getElementById(`rewriteModal${String(num).replace(' ', '')}`);
|
||||
const instruction = modal.querySelector('textarea[name="instruction"]').value;
|
||||
|
||||
if (!instruction) {
|
||||
alert("Please provide an instruction for the AI.");
|
||||
return;
|
||||
}
|
||||
|
||||
const modalInstance = bootstrap.Modal.getInstance(modal);
|
||||
modalInstance.hide();
|
||||
|
||||
const progressModal = new bootstrap.Modal(document.getElementById('progressModal'));
|
||||
progressModal.show();
|
||||
|
||||
const data = {
|
||||
book_folder: "{{ book_folder }}",
|
||||
chapter_num: num,
|
||||
instruction: instruction
|
||||
};
|
||||
|
||||
fetch(`/project/{{ run.id }}/rewrite_chapter`, {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify(data)
|
||||
})
|
||||
.then(res => {
|
||||
if (!res.ok) throw new Error("Failed to start rewrite task.");
|
||||
return res.json();
|
||||
})
|
||||
.then(data => {
|
||||
if (data.task_id) {
|
||||
rewritePollInterval = setInterval(() => pollRewriteStatus(data.task_id), 3000);
|
||||
} else {
|
||||
throw new Error("Did not receive task ID.");
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
progressModal.hide();
|
||||
alert("Error: " + err.message);
|
||||
});
|
||||
}
|
||||
|
||||
function pollRewriteStatus(taskId) {
|
||||
fetch(`/task_status/${taskId}`)
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
if (data.status === 'completed') {
|
||||
clearInterval(rewritePollInterval);
|
||||
setTimeout(() => { window.location.reload(); }, 500);
|
||||
}
|
||||
})
|
||||
.catch(err => { clearInterval(rewritePollInterval); alert("Error checking status. Please reload manually."); });
|
||||
}
|
||||
|
||||
function saveChapter(num) {
|
||||
const content = document.getElementById(`text-${num}`).value;
|
||||
const btn = event.target;
|
||||
const originalText = btn.innerText;
|
||||
|
||||
btn.disabled = true;
|
||||
btn.innerText = "Saving...";
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('book_folder', "{{ book_folder }}");
|
||||
formData.append('chapter_num', num);
|
||||
formData.append('content', content);
|
||||
|
||||
fetch(`/project/{{ run.id }}/save_chapter`, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
}).then(res => {
|
||||
if (res.ok) {
|
||||
alert("Chapter saved! Reloading to render changes...");
|
||||
window.location.reload();
|
||||
} else {
|
||||
alert("Error saving chapter.");
|
||||
btn.disabled = false;
|
||||
btn.innerText = originalText;
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user