RegInsights SEO Article Writer
Generate now or schedule for later. Output is WordPress-ready HTML with H2/H3, meta, slug, outline and stored drafts.
Tone
Professional, practical, human (default)
Conversational but professional
Formal and analytical
Story-driven and inspirational
Approved facts (to prevent hallucinations)
Internal links map (one per line: phrase | URL)
External links map (one per line: phrase | URL)
Generate now
Schedule
Refresh jobs
SEO Title
Copy
Meta Description
Copy
Focus keyword + slug
Copy slug
Outline
Copy
Article editor (WordPress-ready HTML)
Copy article
Post now
Download .doc
B
I
H2
H3
• List
1. List
Link
Clear
Saved jobs
Generated and scheduled drafts are stored in KV.
Created
Scheduled
Status
Title
Keyword
Actions
${html}`;
const blob = new Blob([doc], { type: "application/msword" });
const a = document.createElement("a");
a.href = URL.createObjectURL(blob);
a.download = `${title || "reginsights-article"}.doc`;
document.body.appendChild(a);
a.click();
URL.revokeObjectURL(a.href);
document.body.removeChild(a);
}
async function generateNow(){
const p = payloadBase();
if(!p.title || !p.keyword){
alert("Title and focus keyword are required.");
return;
}
btnGenerate.disabled = true;
elStatus.textContent = "Generating…";
try{
const data = await api("/api/generate", {
method:"POST",
headers:{ "Content-Type":"application/json" },
body: JSON.stringify(p)
});
setOutput(data.job);
elStatus.textContent = "Done. Edit the article then copy into WordPress.";
await refreshJobs();
}catch(e){
elStatus.textContent = "Failed. Try again.";
alert(e.message);
}finally{
btnGenerate.disabled = false;
}
}
async function scheduleJob(){
const p = payloadBase();
if(!p.title || !p.keyword){
alert("Title and focus keyword are required.");
return;
}
if(!elSchedule.value){
alert("Pick a schedule date/time first.");
return;
}
p.scheduledFor = new Date(elSchedule.value).toISOString();
btnSched.disabled = true;
elStatus.textContent = "Scheduling…";
try{
const data = await api("/api/schedule", {
method:"POST",
headers:{ "Content-Type":"application/json" },
body: JSON.stringify(p)
});
elStatus.textContent = `Scheduled. Job ID: ${data.job.id}`;
await refreshJobs();
}catch(e){
elStatus.textContent = "Scheduling failed.";
alert(e.message);
}finally{
btnSched.disabled = false;
}
}
async function refreshJobs(){
const data = await api("/api/jobs?limit=40", { method:"GET" });
const jobs = data.jobs || [];
jobsBody.innerHTML = jobs.map(j => {
const cls = j.status === "done" ? "ri-done" :
j.status === "queued" ? "ri-queued" :
j.status === "running" ? "ri-running" : "ri-error";
return `
${new Date(j.created_at).toLocaleString()}
${j.scheduled_for ? new Date(j.scheduled_for).toLocaleString() : "-"}
${j.status}
${escapeHtml(j.title || "")}
${escapeHtml(j.keyword || "")}
Load
Run now
`;
}).join("");
// wire buttons
jobsBody.querySelectorAll("[data-load]").forEach(b => {
b.addEventListener("click", async () => {
const id = b.getAttribute("data-load");
const one = await api(`/api/jobs/${id}`, { method:"GET" });
if(one.job) setOutput(one.job);
});
});
jobsBody.querySelectorAll("[data-run]").forEach(b => {
b.addEventListener("click", async () => {
const id = b.getAttribute("data-run");
elStatus.textContent = "Running job…";
const out = await api(`/api/jobs/${id}/run`, { method:"POST" });
if(out.job) setOutput(out.job);
elStatus.textContent = "Done.";
await refreshJobs();
});
});
}
function escapeHtml(s){
return String(s || "")
.replace(/&/g,"&").replace(//g,">").replace(/"/g,""");
}
// Toolbar
root.addEventListener("click", (e) => {
const btn = e.target.closest(".ri-toolbtn");
if(!btn) return;
const cmd = btn.getAttribute("data-cmd");
editor.focus();
if(cmd === "bold" || cmd === "italic"){
document.execCommand(cmd, false, null);
} else if(cmd === "h2" || cmd === "h3"){
document.execCommand("formatBlock", false, cmd);
} else if(cmd === "ul"){
document.execCommand("insertUnorderedList", false, null);
} else if(cmd === "ol"){
document.execCommand("insertOrderedList", false, null);
} else if(cmd === "link"){
const u = prompt("Enter URL:");
if(u) document.execCommand("createLink", false, u);
} else if(cmd === "clear"){
document.execCommand("removeFormat", false, null);
}
});
// Copy buttons
root.addEventListener("click", async (e) => {
const btn = e.target.closest(".ri-copy");
if(!btn) return;
const type = btn.getAttribute("data-copy");
try{
if(type === "seoTitle") await navigator.clipboard.writeText(outSeoTitle.textContent || "");
if(type === "metaDescription") await navigator.clipboard.writeText(outMeta.textContent || "");
if(type === "outline") await navigator.clipboard.writeText(outOutline.value || "");
if(type === "slug") await navigator.clipboard.writeText(outSlug.textContent || "");
if(type === "article") await copyHtml();
if(type){
const old = btn.textContent;
btn.textContent = "Copied";
setTimeout(()=>btn.textContent = old, 900);
}
}catch{
alert("Copy failed - select and copy manually.");
}
});
async function postNow(){
if(!editor.innerText.trim()){
alert("No article content yet.");
return;
}
try{ await copyHtml(); }catch{}
const wp = elEditorUrl.value.trim();
if(wp) window.open(wp, "_blank");
else alert("Article copied. Set WordPress editor URL to open it automatically next time.");
}
btnGenerate.addEventListener("click", generateNow);
btnSched.addEventListener("click", scheduleJob);
btnRefresh.addEventListener("click", refreshJobs);
btnPostNow.addEventListener("click", postNow);
btnDownload.addEventListener("click", downloadDoc);
// init
refreshJobs();
})();