Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 37 additions & 2 deletions public/app.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
(function () {
document.querySelectorAll("[data-copy]").forEach((btn) => {
function bindCopyButton(btn) {
const original = btn.textContent;
btn.addEventListener("click", async () => {
const text = btn.getAttribute("data-copy") || "";
Expand All @@ -14,5 +14,40 @@
btn.textContent = original;
}, 1600);
});
});
}

document.querySelectorAll("[data-copy]").forEach(bindCopyButton);

const input = document.querySelector("[data-builder-input]");
const command = document.querySelector("[data-builder-command]");
const copy = document.querySelector("[data-builder-copy]");
const templates = Array.from(document.querySelectorAll("[data-template]"));

function commandFor(value) {
const text = value.trim();
const url = text.match(/https?:\/\/[^\s"']+/i)?.[0];
if (url) return `npx proofloop target --url ${url} --write-runner-plan`;
return "npx proofloop target --write-runner-plan";
}

function updateBuilder() {
if (!input || !command || !copy) return;
const next = commandFor(input.value || "");
command.textContent = next;
copy.setAttribute("data-copy", next);
}

if (input && command && copy) {
input.addEventListener("input", updateBuilder);
templates.forEach((template) => {
template.addEventListener("click", () => {
templates.forEach((item) => item.removeAttribute("data-selected"));
template.setAttribute("data-selected", "true");
input.value = template.getAttribute("data-template") || "";
input.focus();
updateBuilder();
});
});
updateBuilder();
}
})();
71 changes: 71 additions & 0 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,77 @@ <h1>The gate decides<br />when it&rsquo;s <span class="accent">done</span>.</h1>
</p>
</section>

<section class="builder" aria-labelledby="builder-title" data-testid="setup-builder">
<p class="eyebrow">Guided setup</p>
<h2 id="builder-title">Build a proof loop</h2>
<p class="builder-copy">
Hey! I&rsquo;m here to help you set up a proof loop in just a couple of minutes.
What app or workflow are you trying to prove? Describe it in your own words,
or tap a template below.
</p>

<label class="prompt-box" for="proofloop-use-case">
<span>Use case</span>
<textarea
id="proofloop-use-case"
data-builder-input
rows="4"
placeholder="Example: users upload a spreadsheet, the agent reconciles invoices, and the dashboard shows the audited totals."
></textarea>
</label>

<div class="template-grid" aria-label="Proof loop templates">
<button
class="template-card"
type="button"
data-template="Live URL checkout flow: open the production app, complete the main user path, click through the proof-critical controls, and record visible receipts."
>
<span>Live URL flow</span>
<small>Click, submit, and verify the real page.</small>
</button>
<button
class="template-card"
type="button"
data-template="Accounting agent: reconcile ledger rows, create journal entries, verify trial balance totals, and keep benchmark scoring separate from product-path proof."
>
<span>Accounting agent</span>
<small>Reconcile, score, and chart receipts.</small>
</button>
<button
class="template-card"
type="button"
data-template="Document memory pipeline: ingest documents, chunk them, extract memory objects, verify receipt counts, and track stage-level failures."
>
<span>Memory pipeline</span>
<small>Documents to memory with receipts.</small>
</button>
<button
class="template-card"
type="button"
data-template="Research copilot: answer a finance or company research question, cite sources, compare alternatives, and produce a deterministic proof report."
>
<span>Research copilot</span>
<small>Sources, claims, and proof reports.</small>
</button>
</div>

<div class="builder-output" aria-live="polite">
<div>
<span>Kickoff command</span>
<code data-builder-command>npx proofloop target --write-runner-plan</code>
</div>
<button
class="copy-btn"
type="button"
data-builder-copy
data-copy="npx proofloop target --write-runner-plan"
data-testid="copy-builder"
>
Copy
</button>
</div>
</section>

<section class="capabilities" aria-label="What the gate does">
<ol>
<li>
Expand Down
150 changes: 150 additions & 0 deletions public/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,139 @@ code {
display: none;
}

.builder {
margin-top: 52px;
padding: 24px;
border: 1px solid var(--line-strong);
border-radius: 10px;
background: linear-gradient(180deg, rgba(255, 255, 255, 0.035), rgba(255, 255, 255, 0.015));
}

.eyebrow {
margin: 0 0 8px;
color: var(--accent-hover);
font-family: var(--mono);
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
}

.builder h2 {
margin: 0;
color: var(--text);
font-size: 26px;
line-height: 1.18;
font-weight: 700;
}

.builder-copy {
margin: 12px 0 20px;
color: var(--text-muted);
font-size: 14px;
line-height: 1.65;
}

.prompt-box {
display: grid;
gap: 8px;
}

.prompt-box span,
.builder-output span {
color: var(--text-dim);
font-family: var(--mono);
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
}

.prompt-box textarea {
width: 100%;
min-height: 112px;
resize: vertical;
border: 1px solid var(--line-strong);
border-radius: 8px;
background: rgba(11, 11, 13, 0.72);
color: var(--text);
font: inherit;
font-size: 14px;
line-height: 1.55;
padding: 12px 14px;
}

.prompt-box textarea::placeholder {
color: var(--text-dim);
}

.prompt-box textarea:focus {
outline: 2px solid var(--accent);
outline-offset: 2px;
}

.template-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 10px;
margin-top: 16px;
}

.template-card {
min-height: 96px;
padding: 14px;
border: 1px solid var(--line);
border-radius: 8px;
background: rgba(255, 255, 255, 0.025);
color: var(--text);
text-align: left;
cursor: pointer;
transition: border-color 0.15s, background 0.15s, transform 0.15s;
}

.template-card:hover,
.template-card:focus-visible,
.template-card[data-selected="true"] {
border-color: var(--accent);
background: var(--accent-tint);
transform: translateY(-1px);
}

.template-card span {
display: block;
font-size: 14px;
font-weight: 700;
}

.template-card small {
display: block;
margin-top: 6px;
color: var(--text-muted);
font-size: 12.5px;
line-height: 1.45;
}

.builder-output {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
margin-top: 18px;
padding: 14px;
border: 1px solid var(--line);
border-radius: 8px;
background: rgba(11, 11, 13, 0.62);
}

.builder-output div {
min-width: 0;
}

.builder-output code {
display: block;
margin-top: 6px;
overflow-x: auto;
white-space: nowrap;
}

.capabilities {
margin-top: 76px;
padding-top: 40px;
Expand Down Expand Up @@ -377,6 +510,23 @@ button:focus-visible {
font-size: 12px;
}

.builder {
padding: 18px;
}

.template-grid {
grid-template-columns: 1fr;
}

.builder-output {
align-items: stretch;
flex-direction: column;
}

.builder-output .copy-btn {
width: 100%;
}

.capabilities {
margin-top: 56px;
}
Expand Down
11 changes: 11 additions & 0 deletions tests/site.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,17 @@ describe("proofloop.live site", () => {
expect(normalizedHtml).toContain("The gate decides");
});

it("includes the guided setup builder with local templates", () => {
expect(normalizedHtml).toContain("Build a proof loop");
expect(normalizedHtml).toContain("Describe it in your own words");
expect(normalizedHtml).toContain("Live URL flow");
expect(normalizedHtml).toContain("Accounting agent");
expect(normalizedHtml).toContain("Memory pipeline");
expect(normalizedHtml).toContain("Research copilot");
expect(script).toContain("data-builder-input");
expect(script).toContain("npx proofloop target --url");
});

it("states the honesty boundary from the CLI without implying an unbuilt hosted backend", () => {
expect(normalizedHtml).toContain("product-path proof");
expect(normalizedHtml).toContain("proxy benchmark proof");
Expand Down
Loading