Copy Engine ships: Suite AI layer (own engine, RM facts + compliance in our system prompt, deterministic link injection), metered generate/meters APIs, /suite/copy tool page; L2 tile now live
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="robots" content="noindex"><meta name="description" content="Copy Engine — the Circle Suite's AI copywriter. Posts, messages, follow-ups and objection replies in the team voice, with your invite link."><title>Copy Engine | The Circle Suite</title><link rel="icon" type="image/png" href="/favicon.png"><link rel="stylesheet" href="/styles.css">
|
||||
<style>
|
||||
.cw{max-width:880px;margin:0 auto;padding:24px 20px 70px}
|
||||
.cw h1{font-size:clamp(26px,4.5vw,36px);margin:6px 0 8px}
|
||||
.cw .lede{color:var(--muted);line-height:1.6;margin-bottom:18px}
|
||||
#cpGate{display:none;border:2px solid var(--gold);border-radius:12px;padding:16px 20px;margin-bottom:18px;line-height:1.6}
|
||||
#cpGate b{color:var(--gold)}
|
||||
.cp-card{background:var(--card,#0d2236);border:1px solid var(--line,#24425d);border-radius:14px;padding:18px 20px}
|
||||
.cp-kinds{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:14px}
|
||||
.cp-kind{border:1px solid var(--line);background:transparent;color:var(--muted);border-radius:999px;
|
||||
padding:7px 14px;font-size:13.5px;font-weight:700;cursor:pointer;transition:.15s}
|
||||
.cp-kind:hover{border-color:var(--teal);color:var(--text)}
|
||||
.cp-kind.on{border-color:var(--teal);background:rgba(78,214,203,.12);color:var(--teal)}
|
||||
label.cp-lab{display:block;font-weight:700;font-size:14px;margin:0 0 6px}
|
||||
#cpBrief{width:100%;min-height:96px;resize:vertical;background:var(--bg,#0b1d2e);border:1px solid var(--line);
|
||||
border-radius:10px;color:var(--text);padding:11px 13px;font-size:15px;font-family:inherit;line-height:1.5}
|
||||
.cp-hint{color:var(--muted);font-size:12.5px;margin-top:6px}
|
||||
.cp-row{display:flex;gap:12px;align-items:center;flex-wrap:wrap;margin-top:14px}
|
||||
#cpMeter{font-size:12.5px;color:var(--muted);margin-left:auto}
|
||||
#cpMeter b{color:var(--text)}
|
||||
#cpOut{display:none;margin-top:18px}
|
||||
#cpText{white-space:pre-wrap;background:var(--bg,#0b1d2e);border:1px solid var(--line);border-left:3px solid var(--gold);
|
||||
border-radius:10px;padding:16px 18px;font-size:15.5px;line-height:1.6}
|
||||
#cpErr{display:none;color:#f0a05a;margin-top:12px;font-size:14px;line-height:1.55}
|
||||
.cp-note{margin-top:20px;font-size:12.5px;color:var(--muted);line-height:1.6;border-top:1px solid var(--line);padding-top:12px}
|
||||
.spin{display:inline-block;width:13px;height:13px;border:2px solid rgba(255,255,255,.25);border-top-color:var(--gold);
|
||||
border-radius:50%;animation:sp .8s linear infinite;vertical-align:-2px;margin-right:7px}
|
||||
@keyframes sp{to{transform:rotate(360deg)}}
|
||||
@media (prefers-reduced-motion:reduce){.spin{animation:none}}
|
||||
</style></head>
|
||||
<body>
|
||||
<header class="wrap nav"><a class="brand" href="/suite"><img class="brand-mark" src="/logo.jpg" alt="The RM Circle" width="42" height="42"><span><span id="brandName">RM Circle</span><small>Copy Engine</small></span></a><div class="nav-actions"><a class="btn btn-secondary hide-mobile" href="/suite">← The Suite</a><a class="btn btn-primary" href="/tools">Promo Tools</a></div></header>
|
||||
<main class="cw">
|
||||
<div class="eyebrow" style="color:var(--gold)">CIRCLE SUITE · LEVEL 2 · ASCENSUS</div>
|
||||
<h1>Copy <span class="gold">Engine.</span></h1>
|
||||
<p class="lede">Tell it what you need and who it's for. It writes in the team's voice, follows our honesty rules, and drops <strong style="color:var(--text)">your</strong> invite link at the end. Read it before you send it — you're the publisher.</p>
|
||||
|
||||
<div id="cpGate"></div>
|
||||
|
||||
<div class="cp-card" id="cpCard">
|
||||
<label class="cp-lab">What do you need?</label>
|
||||
<div class="cp-kinds" id="cpKinds"></div>
|
||||
<label class="cp-lab" for="cpBrief">What's it about — and who's it for?</label>
|
||||
<textarea id="cpBrief" placeholder="e.g. A post for people who've been burned by side hustles before — honest, no hype, mention that everything is verifiable on-chain"></textarea>
|
||||
<div class="cp-hint">The more specific you are — the person, the angle, the situation — the better it writes. Mention any detail you want included.</div>
|
||||
<div class="cp-row">
|
||||
<button id="cpGo" class="btn btn-primary">✍️ Write it</button>
|
||||
<button id="cpAgain" class="btn btn-secondary" style="display:none">Try another version</button>
|
||||
<span id="cpMeter"></span>
|
||||
</div>
|
||||
<div id="cpErr"></div>
|
||||
<div id="cpOut">
|
||||
<div id="cpText"></div>
|
||||
<div class="cp-row"><button id="cpCopy" class="btn btn-teal btn-sm">📋 Copy</button><span class="cp-hint" id="cpCopied" style="display:none;color:var(--teal)">Copied — go post it.</span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="cp-note"><strong style="color:var(--text)">Teach it forward:</strong> when this saves you fifteen minutes, show your two how to use it — that's how the whole team gets faster. <br>Everything the engine writes follows the team's rules: no income promises, no invented numbers, honest about risk. It's still your name on the post, so read before you send. Independent team resource · No income is guaranteed · Cryptocurrency involves risk.</p>
|
||||
</main>
|
||||
<script src="/suite-copy.js"></script>
|
||||
<script src="/chat.js" defer></script>
|
||||
<script src="/translate.js" defer></script><script src="/tg-app.js" defer></script><script src="/nav-dash.js" defer></script></body></html>
|
||||
@@ -0,0 +1,126 @@
|
||||
// Copy Engine client. Entitlement + meter come from the server; the page just
|
||||
// collects a brief, streams nothing (single response), and shows the result.
|
||||
(function () {
|
||||
'use strict';
|
||||
var KINDS = [
|
||||
{ k: 'post', label: 'Social post' },
|
||||
{ k: 'dm', label: 'Direct message' },
|
||||
{ k: 'followup', label: 'Follow-up' },
|
||||
{ k: 'objection', label: 'Objection reply' },
|
||||
{ k: 'email', label: 'Email' },
|
||||
{ k: 'story', label: 'Personal story post' }
|
||||
];
|
||||
var PLACEHOLDERS = {
|
||||
post: "e.g. A post for people who've been burned by side hustles before — honest, no hype, mention that everything is verifiable on-chain",
|
||||
dm: "e.g. A message to an old coworker who's always complaining about money but is skeptical of crypto",
|
||||
followup: "e.g. Following up with someone who watched the video last week and said 'let me think about it'",
|
||||
objection: "e.g. They said: isn't this just a pyramid scheme?",
|
||||
email: "e.g. Email to my list introducing the team build — they know me from my marketing content, not crypto",
|
||||
story: "e.g. Why I stopped chasing complicated funnels and went back to something simple I could teach"
|
||||
};
|
||||
var $ = function (id) { return document.getElementById(id); };
|
||||
var kind = 'post', busy = false, lastBrief = '';
|
||||
|
||||
function renderKinds() {
|
||||
var host = $('cpKinds');
|
||||
host.innerHTML = '';
|
||||
KINDS.forEach(function (x) {
|
||||
var b = document.createElement('button');
|
||||
b.type = 'button';
|
||||
b.className = 'cp-kind' + (x.k === kind ? ' on' : '');
|
||||
b.textContent = x.label;
|
||||
b.addEventListener('click', function () {
|
||||
kind = x.k;
|
||||
$('cpBrief').placeholder = PLACEHOLDERS[kind] || '';
|
||||
renderKinds();
|
||||
});
|
||||
host.appendChild(b);
|
||||
});
|
||||
}
|
||||
|
||||
function showMeter(m) {
|
||||
if (!m || !m.limit) { $('cpMeter').textContent = ''; return; }
|
||||
$('cpMeter').innerHTML = '<b>' + m.remaining + '</b> of ' + m.limit + ' generations left this month';
|
||||
}
|
||||
|
||||
function gate(msg, gold) {
|
||||
var g = $('cpGate');
|
||||
g.style.display = 'block';
|
||||
g.innerHTML = msg;
|
||||
if (gold === false) g.style.borderColor = 'var(--teal)';
|
||||
$('cpCard').style.opacity = '.55';
|
||||
$('cpGo').disabled = true;
|
||||
$('cpBrief').disabled = true;
|
||||
}
|
||||
|
||||
async function boot() {
|
||||
renderKinds();
|
||||
try {
|
||||
var r = await fetch('/api/public/suite-meters');
|
||||
if (r.status === 401) {
|
||||
gate('You’re not signed in yet. <a href="/suite" style="color:var(--gold);font-weight:700">Open the Suite</a> and connect the wallet that holds your position — one free signature — then come back.');
|
||||
return;
|
||||
}
|
||||
if (r.status === 403) {
|
||||
gate('The Copy Engine isn’t open for this position yet. <a href="/suite" style="color:var(--gold);font-weight:700">See your Suite</a>.');
|
||||
return;
|
||||
}
|
||||
if (!r.ok) return;
|
||||
var d = await r.json();
|
||||
var m = d.meters && d.meters.copy;
|
||||
if (m && m.reason === 'locked') {
|
||||
gate('The Copy Engine unlocks at <b>Ascensus (level 2)</b>. You’re at level ' + d.level + ' — your next upgrade opens it, along with the Page Builder. <a href="/how-pay-works#exact-costs" style="color:var(--gold);font-weight:700">See what an upgrade costs →</a>');
|
||||
return;
|
||||
}
|
||||
showMeter(m);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
async function run() {
|
||||
if (busy) return;
|
||||
var brief = $('cpBrief').value.trim();
|
||||
if (brief.length < 3) { $('cpBrief').focus(); return; }
|
||||
lastBrief = brief;
|
||||
busy = true;
|
||||
$('cpErr').style.display = 'none';
|
||||
$('cpGo').disabled = true;
|
||||
$('cpGo').innerHTML = '<span class="spin"></span>Writing… (up to a minute)';
|
||||
try {
|
||||
var r = await fetch('/api/public/suite-generate', {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ kind: kind, brief: brief })
|
||||
});
|
||||
var d = await r.json();
|
||||
if (!r.ok) {
|
||||
$('cpErr').textContent = d.error || 'That didn’t go through — try again.';
|
||||
$('cpErr').style.display = 'block';
|
||||
if (d.meter) showMeter(d.meter);
|
||||
} else {
|
||||
$('cpText').textContent = d.text;
|
||||
$('cpOut').style.display = 'block';
|
||||
$('cpAgain').style.display = '';
|
||||
showMeter(d.meter);
|
||||
$('cpOut').scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||||
}
|
||||
} catch (e) {
|
||||
$('cpErr').textContent = 'Connection hiccup — try again.';
|
||||
$('cpErr').style.display = 'block';
|
||||
}
|
||||
busy = false;
|
||||
$('cpGo').disabled = false;
|
||||
$('cpGo').textContent = '✍️ Write it';
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
boot();
|
||||
$('cpGo').addEventListener('click', run);
|
||||
$('cpAgain').addEventListener('click', run);
|
||||
$('cpCopy').addEventListener('click', function () {
|
||||
var t = $('cpText').textContent;
|
||||
navigator.clipboard.writeText(t).then(function () {
|
||||
$('cpCopied').style.display = '';
|
||||
setTimeout(function () { $('cpCopied').style.display = 'none'; }, 2500);
|
||||
});
|
||||
});
|
||||
});
|
||||
})();
|
||||
+1
-1
@@ -13,7 +13,7 @@
|
||||
{ lv: 1, ico: '🎓', name: 'The Circle Method', desc: 'The 10-lesson recruiting and coaching course. Lessons unlock with this same wallet.', href: '/training', live: true },
|
||||
{ lv: 1, ico: '📈', name: 'Live Dashboard', desc: 'Your pipeline, organization bar, coach panel and wallet-verified team messages.', href: '/my', live: true },
|
||||
{ lv: 1, ico: '🤖', name: 'AI Coach', desc: '24/7 answers about the system, in 21+ languages — the chat bubble on every page of this site.', href: '#', live: true, coach: true },
|
||||
{ lv: 2, ico: '✍️', name: 'Copy Engine', desc: 'An AI copywriter tuned to this business: posts, DMs, follow-ups and objection replies with your link filled in.', live: false },
|
||||
{ lv: 2, ico: '✍️', name: 'Copy Engine', desc: 'An AI copywriter tuned to this business: posts, DMs, follow-ups and objection replies with your link filled in.', href: '/suite/copy', live: true },
|
||||
{ lv: 2, ico: '🧱', name: 'Page Builder', desc: 'Answer five questions, get your own hosted bridge page — your story, your angle video, your QR.', live: false },
|
||||
{ lv: 3, ico: '📧', name: 'Email Engine', desc: 'Follow-up sequences and broadcasts in the team voice, exportable to any autoresponder.', live: false },
|
||||
{ lv: 3, ico: '🎥', name: 'Video Maker', desc: 'The team’s master promo videos rendered with your personal end-card — your name, your QR, your link.', live: false },
|
||||
|
||||
Reference in New Issue
Block a user