AI composer
Where a person asks an assistant something: attached sources (one still reading), a mode switch, and a send button that stays quiet until there is text.
RunningStarted 9:41 am · 1m 12s so far
match_invoices(po="PO-3318")
INV-2201 Inchley Print Co. $2,480.00 matched
INV-2204 Pendrick Telecom $612.40 matched
INV-2207 Yarrowby Software $3,940.00 over PO by $1,140.00It is live: hover, focus and its own controls respond. Actions that would change data (approve, send, delete) are for your app to wire. The width is a real 390 px, 768 px or 1280 px box, shrunk to fit your screen.
<style>
.odl-agent-steps { box-sizing: border-box; inline-size: 100%; max-inline-size: 560px; container-type: inline-size; display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--ds-space-stack-md);
padding: var(--ds-space-inset-lg); background: var(--ds-color-surface-raised); color: var(--ds-color-text-primary); border: var(--ds-border-width-default) solid var(--ds-color-border-subtle);
border-radius: var(--ds-radius-container); box-shadow: var(--ds-elevation-raised); font: var(--ds-text-body-md); text-wrap: pretty; }
.odl-agent-steps *, .odl-agent-steps *::before, .odl-agent-steps *::after { box-sizing: border-box; }
.odl-agent-steps .odl-head { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: start; gap: 0 var(--ds-space-inline-sm); padding-block-end: var(--ds-space-stack-md); border-block-end: var(--ds-border-width-default) solid var(--ds-color-border-subtle); }
.odl-agent-steps h3 { margin: 0; font: var(--ds-text-title-sm); align-self: center; }
.odl-agent-steps .odl-sub { grid-column: 1 / -1; display: flex; flex-wrap: wrap; align-items: center; gap: var(--ds-space-2) var(--ds-space-inline-sm); margin: var(--ds-space-stack-xs) 0 0; color: var(--ds-color-text-secondary);
font: var(--ds-text-body-sm); font-variant-numeric: tabular-nums; }
.odl-agent-steps .odl-run { display: inline-flex; align-items: center; gap: var(--ds-space-inline-xs); padding: var(--ds-space-2) var(--ds-space-8); border-radius: var(--ds-radius-control-sm);
background: var(--ds-color-bg-accent-subtle); color: var(--ds-color-text-accent); font: var(--ds-text-label-md); font-weight: var(--ds-font-weight-label); }
.odl-agent-steps .odl-run::before { content: ""; inline-size: var(--ds-space-6); block-size: var(--ds-space-6); border-radius: var(--ds-radius-full); background: currentColor;
animation: odl-agent-steps-pulse var(--ds-motion-duration-extra) var(--ds-motion-easing-standard) infinite alternate; }
.odl-agent-steps[data-run="stopped"] .odl-run { background: var(--ds-color-bg-danger-subtle); color: var(--ds-color-text-danger); }
.odl-agent-steps[data-run="stopped"] .odl-run::before { animation: none; }
@keyframes odl-agent-steps-pulse { to { opacity: 0.35; } }
.odl-agent-steps .odl-stop { flex: none; display: inline-flex; align-items: center; gap: var(--ds-space-inline-xs); min-block-size: max(var(--ds-size-control-sm), var(--ds-size-target-min));
padding: 0 var(--ds-space-inset-sm); border: var(--ds-border-width-default) solid var(--ds-color-border-default); border-radius: var(--ds-radius-control); background: var(--ds-color-surface-raised);
color: var(--ds-color-text-danger); font: var(--ds-text-label-lg); font-weight: var(--ds-font-weight-label); cursor: pointer; white-space: nowrap;
transition: background-color var(--ds-motion-duration-short) var(--ds-motion-easing-standard); }
.odl-agent-steps .odl-stop:hover { background: var(--ds-color-bg-danger-subtle); }
.odl-agent-steps .odl-stop svg { inline-size: var(--ds-size-icon-sm); block-size: var(--ds-size-icon-sm); }
.odl-agent-steps .odl-stop:disabled { color: var(--ds-color-text-disabled); background: var(--ds-color-bg-disabled); border-color: var(--ds-color-border-subtle); cursor: default; }
.odl-agent-steps ol { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: minmax(0, 1fr); }
.odl-agent-steps li { position: relative; display: grid; grid-template-columns: var(--ds-size-icon-md) minmax(0, 1fr) auto; column-gap: var(--ds-space-inline-sm); padding-block-end: var(--ds-space-stack-md); }
.odl-agent-steps li:last-child { padding-block-end: 0; }
.odl-agent-steps li:not(:last-child)::before { content: ""; position: absolute; inset-block: calc(var(--ds-size-icon-md) + (1lh - var(--ds-size-icon-md)) / 2 + var(--ds-space-4)) var(--ds-space-4); inset-inline-start: calc(var(--ds-size-icon-md) / 2 - var(--ds-border-width-default) / 2);
border-inline-start: var(--ds-border-width-default) solid var(--ds-color-border-subtle); }
.odl-agent-steps .odl-dot { margin-block-start: calc((1lh - var(--ds-size-icon-md)) / 2); display: grid; place-items: center; inline-size: var(--ds-size-icon-md); block-size: var(--ds-size-icon-md); border-radius: var(--ds-radius-full);
border: var(--ds-border-width-emphasis) solid var(--ds-color-border-default); background: var(--ds-color-surface-raised); color: var(--ds-color-icon-on-accent); }
.odl-agent-steps .odl-dot svg { inline-size: 75%; block-size: 75%; }
.odl-agent-steps .is-done .odl-dot { background: var(--ds-color-bg-success-bold); border-color: var(--ds-color-bg-success-bold); }
.odl-agent-steps .is-running .odl-dot { border-color: var(--ds-color-border-accent); }
.odl-agent-steps .is-running .odl-dot::after { content: ""; inline-size: 40%; block-size: 40%; border-radius: var(--ds-radius-full); background: var(--ds-color-bg-accent-bold); }
.odl-agent-steps .is-waiting .odl-dot { background: var(--ds-color-bg-warning-subtle); border-color: var(--ds-color-border-warning); color: var(--ds-color-text-warning); }
.odl-agent-steps .is-skipped .odl-dot { background: var(--ds-color-bg-neutral-subtle); border-color: var(--ds-color-border-subtle); color: var(--ds-color-text-secondary); }
.odl-agent-steps .is-stopped .odl-dot { background: var(--ds-color-bg-danger-subtle); border-color: var(--ds-color-border-danger); color: var(--ds-color-text-danger); }
.odl-agent-steps .odl-text { min-inline-size: 0; display: grid; gap: var(--ds-space-2); }
.odl-agent-steps .odl-title { font-weight: var(--ds-font-weight-label); }
.odl-agent-steps .is-skipped .odl-title { color: var(--ds-color-text-secondary); font-weight: var(--ds-font-weight-body); }
.odl-agent-steps .odl-detail { color: var(--ds-color-text-secondary); font: var(--ds-text-body-sm); }
.odl-agent-steps .odl-time { color: var(--ds-color-text-tertiary); font: var(--ds-text-code-sm); font-variant-numeric: tabular-nums; white-space: nowrap;
display: flex; align-items: center; block-size: calc(var(--ds-text-body-md-size) * var(--ds-text-body-md-line-height)); }
.odl-agent-steps .odl-tag { justify-self: start; display: inline-flex; align-items: center; padding: var(--ds-space-2) var(--ds-space-8); border-radius: var(--ds-radius-control-sm); font: var(--ds-text-label-md); }
.odl-agent-steps .is-waiting .odl-tag { background: var(--ds-color-bg-warning-subtle); color: var(--ds-color-text-warning); }
.odl-agent-steps .odl-progress { position: relative; block-size: var(--ds-space-4); margin-block-start: var(--ds-space-4); max-inline-size: 16rem; overflow: hidden; border-radius: var(--ds-radius-full); background: var(--ds-color-bg-neutral-subtle); }
.odl-agent-steps .odl-progress span { position: absolute; inset-block: 0; inset-inline-start: 0; inline-size: 66%; border-radius: inherit; background: var(--ds-color-bg-accent-bold); }
.odl-agent-steps .odl-progress span::after { content: ""; position: absolute; inset: 0; background: linear-gradient(90deg, transparent, var(--ds-color-surface-raised), transparent); opacity: 0.5;
animation: odl-agent-steps-sheen var(--ds-motion-duration-extra) var(--ds-motion-easing-linear) infinite; transform: translateX(-100%); }
@keyframes odl-agent-steps-sheen { to { transform: translateX(100%); } }
.odl-agent-steps .odl-approve { display: flex; flex-wrap: wrap; gap: var(--ds-space-inline-sm); margin-block-start: var(--ds-space-stack-xs); }
.odl-agent-steps .odl-approve button { display: inline-flex; align-items: center; gap: var(--ds-space-inline-xs); min-block-size: max(var(--ds-size-control-sm), var(--ds-size-target-min));
padding: 0 var(--ds-space-inset-sm); border-radius: var(--ds-radius-control); font: var(--ds-text-label-lg); font-weight: var(--ds-font-weight-label); cursor: pointer;
transition: background-color var(--ds-motion-duration-short) var(--ds-motion-easing-standard); }
.odl-agent-steps .odl-approve svg { inline-size: var(--ds-size-icon-sm); block-size: var(--ds-size-icon-sm); }
.odl-agent-steps .odl-yes { background: var(--ds-color-bg-action-primary); color: var(--ds-color-text-on-action); border: var(--ds-border-width-default) solid var(--ds-color-bg-action-primary); }
.odl-agent-steps .odl-yes:hover { background: var(--ds-color-bg-action-primary-hover); }
.odl-agent-steps .odl-no { background: var(--ds-color-surface-raised); color: var(--ds-color-text-primary); border: var(--ds-border-width-default) solid var(--ds-color-border-default); }
.odl-agent-steps .odl-no:hover { background: var(--ds-color-bg-neutral-subtle); }
.odl-agent-steps details { margin-block-start: var(--ds-space-4); }
.odl-agent-steps summary { display: inline-flex; align-items: center; gap: var(--ds-space-inline-xs); min-block-size: var(--ds-size-target-min); padding-inline-end: var(--ds-space-8);
color: var(--ds-color-text-link); font: var(--ds-text-label-lg); cursor: pointer; list-style: none; border-radius: var(--ds-radius-control-sm); }
.odl-agent-steps summary::-webkit-details-marker { display: none; }
.odl-agent-steps summary svg { inline-size: var(--ds-size-icon-sm); block-size: var(--ds-size-icon-sm); transition: transform var(--ds-motion-duration-short) var(--ds-motion-easing-standard); }
.odl-agent-steps details[open] summary svg { transform: rotate(90deg); }
.odl-agent-steps pre { margin: 0; padding: var(--ds-space-inset-sm); background: var(--ds-color-surface-sunken); border: var(--ds-border-width-default) solid var(--ds-color-border-subtle);
border-radius: var(--ds-radius-nested); color: var(--ds-color-text-primary); font: var(--ds-text-code-sm); white-space: pre-wrap; overflow-wrap: anywhere; }
.odl-agent-steps pre .odl-k { color: var(--ds-color-text-secondary); }
.odl-agent-steps pre .odl-ok { color: var(--ds-color-text-success); }
.odl-agent-steps pre .odl-warn { color: var(--ds-color-text-warning); }
.odl-agent-steps button:focus-visible, .odl-agent-steps summary:focus-visible { outline: var(--ds-focus-ring-width) solid var(--ds-color-border-focus); outline-offset: var(--ds-focus-ring-offset); }
.odl-agent-steps [hidden] { display: none !important; }
@container (max-width: 24rem) { .odl-agent-steps { padding: var(--ds-space-inset-md); } }
@media (prefers-reduced-motion: reduce) { .odl-agent-steps, .odl-agent-steps *, .odl-agent-steps *::before, .odl-agent-steps *::after { transition: none !important; animation: none !important; } }
</style>
<section class="odl-agent-steps" data-run="running" aria-labelledby="odl-agent-steps-title">
<div class="odl-head">
<h3 id="odl-agent-steps-title">Match October invoices</h3>
<button type="button" class="odl-stop"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><rect x="4" y="4" width="8" height="8" rx="1.5"/></svg>Stop run</button>
<p class="odl-sub"><span class="odl-run" role="status">Running</span><span>Started 9:41 am · 1m 12s so far</span></p>
</div>
<ol>
<li class="is-done"><span class="odl-dot"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="m3.5 8.5 3 3 6-7"/></svg></span>
<span class="odl-text"><span class="odl-title">Read 3 invoices</span><span class="odl-detail">Inchley Print Co., Yarrowby Software, Pendrick Telecom</span></span><span class="odl-time">0:08</span></li>
<li class="is-done"><span class="odl-dot"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="m3.5 8.5 3 3 6-7"/></svg></span>
<span class="odl-text"><span class="odl-title">Matched 2 to PO-3318</span><span class="odl-detail">Amounts and quantities agree within $0.50</span>
<details><summary><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><path d="m6 4 4 4-4 4"/></svg>Tool output</summary>
<pre><code><span class="odl-k">match_invoices(po="PO-3318")</span>
INV-2201 Inchley Print Co. $2,480.00 <span class="odl-ok">matched</span>
INV-2204 Pendrick Telecom $612.40 <span class="odl-ok">matched</span>
INV-2207 Yarrowby Software $3,940.00 <span class="odl-warn">over PO by $1,140.00</span></code></pre></details></span><span class="odl-time">0:21</span></li>
<li class="is-running" aria-current="step"><span class="odl-dot"></span>
<span class="odl-text"><span class="odl-title">Drafting the payment batch</span><span class="odl-detail">2 of 3 payments drafted</span><span class="odl-progress" role="progressbar" aria-label="Payment batch" aria-valuemin="0" aria-valuemax="3" aria-valuenow="2"><span></span></span></span><span class="odl-time">0:34</span></li>
<li class="is-waiting"><span class="odl-dot"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><path d="M8 4.5V8l2 1.25"/></svg></span>
<span class="odl-text"><span class="odl-title">Flag 1 for review</span><span class="odl-detail">INV-2207 is $1,140.00 over the PO. Send it to Bettina Marangoni?</span><span class="odl-tag">Waiting for your approval</span>
<span class="odl-approve"><button type="button" class="odl-yes"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="m3.5 8.5 3 3 6-7"/></svg>Approve</button><button type="button" class="odl-no">Deny</button></span></span><span class="odl-time">0:09</span></li>
<li class="is-skipped"><span class="odl-dot"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><path d="M4.5 8h7"/></svg></span>
<span class="odl-text"><span class="odl-title">Email vendors a remittance note</span><span class="odl-detail">Skipped: turned off for this workspace</span></span><span class="odl-time"></span></li>
</ol>
</section>
<script>
document.querySelectorAll(".odl-agent-steps").forEach((root) => {
if (root.dataset.odlReady) return; // wire once, even if the file is pasted twice
root.dataset.odlReady = "1";
const stop = root.querySelector(".odl-stop"), run = root.querySelector(".odl-run");
const wait = root.querySelector(".is-waiting"), tag = wait.querySelector(".odl-tag"), btns = wait.querySelector(".odl-approve");
const settle = (cls, text, d, w) => { wait.className = cls; tag.className = "odl-detail"; tag.textContent = text; btns.hidden = true;
const svg = wait.querySelector(".odl-dot svg"); svg.setAttribute("stroke-width", w); svg.querySelector("path").setAttribute("d", d); };
wait.querySelector(".odl-yes").addEventListener("click", () => settle("is-done", "Approved by you. Sent to Bettina Marangoni.", "m3.5 8.5 3 3 6-7", "2"));
wait.querySelector(".odl-no").addEventListener("click", () => settle("is-skipped", "Denied by you. INV-2207 stays on hold.", "M4.5 8h7", "1.5"));
stop.addEventListener("click", () => {
root.dataset.run = "stopped"; run.textContent = "Stopped"; stop.disabled = true;
const cur = root.querySelector(".is-running");
if (cur) { cur.className = "is-stopped"; cur.removeAttribute("aria-current"); const bar = cur.querySelector(".odl-progress"); if (bar) bar.hidden = true; cur.querySelector(".odl-detail").textContent = "Stopped after 2 of 3 payments. Nothing was sent."; }
if (!btns.hidden) settle("is-skipped", "Not sent: the run was stopped.", "M4.5 8h7", "1.5");
});
});
</script>
Where a person asks an assistant something: attached sources (one still reading), a mode switch, and a send button that stays quiet until there is text.
A suggested rewrite of one sentence inside a text field, shown as a diff with the reason and where it came from, to accept, reject or swap for another.
One assistant turn: the question, an answer still being written with numbered citations, the sources it cites with page numbers, and copy, retry and rating actions.