Files
bookapp/templates/persona_edit.html
Mike Wichers f7099cc3e4 v2.0.0: Modularize project into single-responsibility packages
Replaced monolithic modules/ package with a clean architecture:

- core/       config.py, utils.py
- ai/         models.py (ResilientModel), setup.py (init_models)
- story/      planner.py, writer.py, editor.py, style_persona.py, bible_tracker.py
- marketing/  cover.py, blurb.py, fonts.py, assets.py
- export/     exporter.py
- web/        app.py (Flask factory), db.py, helpers.py, tasks.py, routes/{auth,project,run,persona,admin}.py
- cli/        engine.py (run_generation), wizard.py (BookWizard)

Flask routes split into 5 Blueprints; all templates updated with blueprint-
prefixed url_for() calls. Dockerfile and docker-compose updated to use
web.app entry point and new package paths.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 22:20:53 -05:00

121 lines
6.1 KiB
HTML

{% extends "base.html" %}
{% block content %}
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card shadow-sm">
<div class="card-header bg-light">
<h4 class="mb-0">{% if name %}Edit Persona: {{ name }}{% else %}New Persona{% endif %}</h4>
</div>
<div class="card-body">
<form action="{{ url_for('persona.save_persona') }}" method="POST">
<input type="hidden" name="old_name" value="{{ name }}">
<div class="mb-3">
<label class="form-label">Name / Pseudonym</label>
<input type="text" name="name" class="form-control" value="{{ persona.get('name', '') }}" required>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Age</label>
<input type="text" name="age" class="form-control" value="{{ persona.get('age', '') }}">
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Gender</label>
<input type="text" name="gender" class="form-control" value="{{ persona.get('gender', '') }}">
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Race / Ethnicity</label>
<input type="text" name="race" class="form-control" value="{{ persona.get('race', '') }}">
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Nationality</label>
<input type="text" name="nationality" class="form-control" value="{{ persona.get('nationality', '') }}">
</div>
</div>
<div class="mb-3">
<label class="form-label">Primary Language</label>
<input type="text" name="language" class="form-control" value="{{ persona.get('language', 'Standard English') }}">
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Voice Keywords</label>
<input type="text" name="voice_keywords" id="voiceKeywords" class="form-control" value="{{ persona.get('voice_keywords', '') }}" placeholder="e.g. Sarcastic, Fast-paced, Minimalist">
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Style Inspirations</label>
<input type="text" name="style_inspirations" id="styleInspirations" class="form-control" value="{{ persona.get('style_inspirations', '') }}" placeholder="e.g. Hemingway, Noir Fiction">
</div>
</div>
<div class="mb-3">
<label class="form-label">Bio / Writing Style</label>
<textarea name="bio" id="bioField" class="form-control" rows="4">{{ persona.get('bio', '') }}</textarea>
<div class="form-text">Describe the voice, tone, and stylistic quirks.</div>
</div>
<div class="mb-3">
<div class="d-flex justify-content-between align-items-center mb-1">
<label class="form-label mb-0">Sample Text (Manual)</label>
<button type="button" class="btn btn-sm btn-outline-success" onclick="analyzePersona()">
<i class="fas fa-magic me-1"></i> Analyze & Auto-Fill
</button>
</div>
<textarea name="sample_text" id="sampleText" class="form-control" rows="6">{{ persona.get('sample_text', '') }}</textarea>
<div class="form-text">Paste a paragraph of text. The AI can analyze this to fill the fields above.</div>
</div>
<div class="d-flex justify-content-between">
<a href="{{ url_for('persona.list_personas') }}" class="btn btn-outline-secondary">Cancel</a>
<button type="submit" class="btn btn-primary">Save Persona</button>
</div>
</form>
</div>
</div>
</div>
</div>
<script>
function analyzePersona() {
const btn = event.target;
const originalText = btn.innerHTML;
btn.disabled = true;
btn.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span> Analyzing...';
const data = {
name: document.querySelector('input[name="name"]').value,
age: document.querySelector('input[name="age"]').value,
gender: document.querySelector('input[name="gender"]').value,
nationality: document.querySelector('input[name="nationality"]').value,
sample_text: document.getElementById('sampleText').value
};
fetch('/persona/analyze', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(data)
})
.then(r => r.json())
.then(resp => {
if (resp.error) {
alert("Error: " + resp.error);
} else {
if (resp.bio) document.getElementById('bioField').value = resp.bio;
if (resp.voice_keywords) document.getElementById('voiceKeywords').value = resp.voice_keywords;
if (resp.style_inspirations) document.getElementById('styleInspirations').value = resp.style_inspirations;
}
})
.catch(err => alert("Request failed."))
.finally(() => {
btn.disabled = false;
btn.innerHTML = originalText;
});
}
</script>
{% endblock %}