diff --git a/dev-api.py b/dev-api.py
index d09b233..98bed9d 100644
--- a/dev-api.py
+++ b/dev-api.py
@@ -667,7 +667,8 @@ def approve_resume(job_id: int, body: dict):
raise HTTPException(400, "preview_struct is required")
from scripts.resume_optimizer import render_resume_text
- final_text = render_resume_text(struct)
+ override = (body.get("preview_text_override") or "").strip()
+ final_text = override if override else render_resume_text(struct)
# Persist plain text + struct (struct enables YAML export later)
_finalize(db_path=db_path, job_id=job_id, final_text=final_text)
diff --git a/scripts/resume_optimizer.py b/scripts/resume_optimizer.py
index 9d0298c..b71268a 100644
--- a/scripts/resume_optimizer.py
+++ b/scripts/resume_optimizer.py
@@ -532,27 +532,37 @@ def apply_review_decisions(
struct["skills"] = sorted(original_kept | approved_additions)
break
- # ── Summary: accept proposed or revert to original ──────────────────────
- if not decisions.get("summary", {}).get("accepted", True):
+ # ── Summary: accept/reject + optional user-edited text ─────────────────
+ summary_dec = decisions.get("summary", {})
+ if not summary_dec.get("accepted", True):
for sec in sections:
if sec["section"] == "summary":
struct["career_summary"] = sec.get("original", struct.get("career_summary", ""))
break
+ else:
+ edited_text = summary_dec.get("edited_text")
+ if edited_text is not None:
+ struct["career_summary"] = edited_text.strip()
- # ── Experience: per-entry accept/reject ─────────────────────────────────
- exp_decisions: dict[str, bool] = {
- f"{ed.get('title', '')}|{ed.get('company', '')}": ed.get("accepted", True)
+ # ── Experience: per-entry accept/reject + optional user-edited bullets ──
+ exp_entry_map: dict[str, dict] = {
+ f"{ed.get('title', '')}|{ed.get('company', '')}": ed
for ed in (decisions.get("experience", {}).get("accepted_entries") or [])
}
for sec in sections:
if sec["section"] == "experience":
for entry_diff in (sec.get("entries") or []):
key = f"{entry_diff['title']}|{entry_diff['company']}"
- if not exp_decisions.get(key, True):
- for exp_entry in (struct.get("experience") or []):
- if (exp_entry.get("title") == entry_diff["title"] and
- exp_entry.get("company") == entry_diff["company"]):
+ entry_dec = exp_entry_map.get(key, {})
+ accepted = entry_dec.get("accepted", True)
+ edited_bullets = entry_dec.get("edited_bullets")
+ for exp_entry in (struct.get("experience") or []):
+ if (exp_entry.get("title") == entry_diff["title"] and
+ exp_entry.get("company") == entry_diff["company"]):
+ if not accepted:
exp_entry["bullets"] = entry_diff["original_bullets"]
+ elif edited_bullets is not None:
+ exp_entry["bullets"] = [b for b in edited_bullets if b.strip()]
break
return struct
diff --git a/web/src/components/ResumeOptimizerPanel.vue b/web/src/components/ResumeOptimizerPanel.vue
index e076ac0..41b84b3 100644
--- a/web/src/components/ResumeOptimizerPanel.vue
+++ b/web/src/components/ResumeOptimizerPanel.vue
@@ -112,16 +112,15 @@
Preview — not yet saved
- Review the assembled resume above. If it looks right, click
- Approve & Save to lock it in. You can also go back and adjust
- your review decisions.
+ Review and edit the assembled resume above. Click
+ Approve & Save to lock it in, or go back to adjust
+ your section-level decisions.