// Editor screen — WYSIWYG resume with highlighted issues
function HighlightedText({ text, issues, onIssueClick, activeId }) {
if (!issues || issues.length === 0) return <>{text}>;
// Build spans. Find each issue word in order.
const parts = [];
let cursor = 0;
const sorted = [...issues].map(iss => ({ ...iss, idx: text.indexOf(iss.word, 0) })).filter(i => i.idx >= 0).sort((a,b) => a.idx - b.idx);
sorted.forEach((iss, n) => {
if (iss.idx > cursor) parts.push({text.slice(cursor, iss.idx)});
parts.push(
onIssueClick(iss)}
className={ISSUE_META[iss.type]?.cls}
style={{
cursor: 'pointer', borderRadius: 3, padding: '0 2px',
outline: activeId === iss.id ? `2px solid ${ISSUE_META[iss.type].color}` : 'none',
outlineOffset: 1,
}}>{iss.word}
);
cursor = iss.idx + iss.word.length;
});
if (cursor < text.length) parts.push({text.slice(cursor)});
return <>{parts}>;
}
function EditorScreen({ platform, onBack, onFix, onIssueTap, onExport, fixedIds = new Set(), activeIssue }) {
const [view, setView] = React.useState('resume'); // resume | list
const issues = allIssues();
const remaining = issues.filter(i => !fixedIds.has(i.id));
const applyIfFixed = (text, issueList) => {
// When the fix-all is applied, swap in the fixed copy.
let out = text;
issueList.forEach(iss => {
if (fixedIds.has(iss.id) && FIXES[iss.id]) {
out = out.replace(FIXES[iss.id].from, FIXES[iss.id].to);
}
});
return out;
};
const liveIssues = (issueList) => issueList.filter(i => !fixedIds.has(i.id));
return (
}
/>
{/* view toggle */}
{['resume','issues'].map(v => (
))}
{view === 'resume' && (
{/* resume paper */}
{/* name */}
{RESUME.name}
{RESUME.title}
{RESUME.contact.map((c,i) => {c})}
{/* summary */}
summary
{/* experience */}
experience
{RESUME.experience.map((exp, ei) => (
{exp.role}, {exp.company}
{exp.dates}
{exp.bullets.map((b, bi) => (
-
))}
))}
{/* education */}
education
{RESUME.education.school} · {RESUME.education.degree}
{/* missing section prompt */}
{liveIssues(RESUME.missingSections.map((s,i) => ({ id: `m${i}`, type: 'missing', word: s }))).length > 0 && (
onIssueTap({ id: 'm0', type: 'missing', word: 'Skills' })}>
+ missing: skills section — tap to add
)}
)}
{view === 'list' && (
{remaining.length === 0 ? (
all fixed ✨
your aura is glowing.
) : (
{remaining.map(iss => (
onIssueTap(iss)} style={{
background: '#fffdf7', borderRadius: 14, padding: '12px 14px',
border: '1px solid rgba(26,13,46,0.08)', display: 'flex', gap: 12, alignItems: 'flex-start',
cursor: 'pointer',
}}>
{ISSUE_META[iss.type].icon}
{ISSUE_META[iss.type].label} · {iss.section}
"{iss.word}"
))}
)}
)}
{/* sticky fix-all + export bar */}
{remaining.length > 0 ? (
<>
fix all {remaining.length}
download
>
) : (
download resume
)}
);
}
window.EditorScreen = EditorScreen;
window.HighlightedText = HighlightedText;