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.
Reply to Inchley Print Co.
Hi Rhea, thanks for flagging the duplicate line on invoice INV-2207.
We will be sending the corrected invoice over to you at some point early next week, once finance has had a chance to look at it. Nothing else on the account changes.
We will be sending We'll send the corrected invoice over to you at some point early next week, once finance has had a chance to look at it. by Tue, Nov 3, after finance reviews it.
We will be sending the corrected invoice over to you at some point early next week, once finance has had a chance to look at it. Finance is reviewing the corrected invoice now; you'll have it by Tue, Nov 3.
Edit applied.
It 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-ai-suggestion { box-sizing: border-box; inline-size: 100%; max-inline-size: 600px; container-type: inline-size; display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--ds-space-stack-sm);
color: var(--ds-color-text-primary); font: var(--ds-text-body-md); text-wrap: pretty; }
.odl-ai-suggestion *, .odl-ai-suggestion *::before { box-sizing: border-box; }
.odl-ai-suggestion .odl-label { margin: 0; color: var(--ds-color-text-secondary); font: var(--ds-text-label-md); letter-spacing: var(--ds-text-label-md-tracking); }
.odl-ai-suggestion .odl-field { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--ds-space-stack-sm); padding: var(--ds-space-inset-md); background: var(--ds-color-bg-field);
border: var(--ds-border-width-default) solid var(--ds-color-border-input); border-radius: var(--ds-radius-container); }
.odl-ai-suggestion .odl-field p { margin: 0; }
.odl-ai-suggestion .odl-target { border-radius: var(--ds-radius-detail); -webkit-box-decoration-break: clone; box-decoration-break: clone; transition: background-color var(--ds-motion-duration-short) var(--ds-motion-easing-standard); }
.odl-ai-suggestion[data-state="open"] .odl-target { background: var(--ds-color-bg-accent-subtle); }
.odl-ai-suggestion[data-state="accepted"] .odl-target { background: var(--ds-color-bg-success-subtle); }
.odl-ai-suggestion .odl-card { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--ds-space-stack-sm); padding: var(--ds-space-inset-md); background: var(--ds-color-surface-raised);
border: var(--ds-border-width-default) solid var(--ds-color-border-subtle); border-radius: var(--ds-radius-container); box-shadow: var(--ds-elevation-floating); }
.odl-ai-suggestion .odl-card-head { display: flex; align-items: center; gap: var(--ds-space-inline-xs); color: var(--ds-color-text-accent); font: var(--ds-text-label-md); }
.odl-ai-suggestion .odl-card-head svg { flex: none; inline-size: var(--ds-size-icon-sm); block-size: var(--ds-size-icon-sm); }
.odl-ai-suggestion .odl-card-head .odl-count { margin-inline-start: auto; color: var(--ds-color-text-tertiary); font-variant-numeric: tabular-nums; }
.odl-ai-suggestion .odl-diff { margin: 0; font: var(--ds-text-body-md); }
.odl-ai-suggestion del { color: var(--ds-color-text-danger); background: var(--ds-color-bg-danger-subtle); text-decoration-thickness: var(--ds-border-width-default); border-radius: var(--ds-radius-detail); padding: 0 var(--ds-space-2); }
.odl-ai-suggestion ins { color: var(--ds-color-text-success); background: var(--ds-color-bg-success-subtle); text-decoration: none; border-radius: var(--ds-radius-detail); padding: 0 var(--ds-space-2); font-weight: var(--ds-font-weight-label); }
.odl-ai-suggestion .odl-meta { display: grid; gap: var(--ds-space-stack-xs); }
.odl-ai-suggestion .odl-why { margin: 0; color: var(--ds-color-text-secondary); font: var(--ds-text-body-sm); }
.odl-ai-suggestion .odl-why strong { color: var(--ds-color-text-primary); font-weight: var(--ds-font-weight-label); }
.odl-ai-suggestion .odl-bar { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: var(--ds-space-inline-xs); padding-block-start: var(--ds-space-stack-sm); border-block-start: var(--ds-border-width-default) solid var(--ds-color-border-subtle); }
.odl-ai-suggestion .odl-note { margin: 0; display: flex; align-items: center; gap: var(--ds-space-inline-xs); min-inline-size: 0; color: var(--ds-color-text-tertiary); font: var(--ds-text-body-sm); }
.odl-ai-suggestion .odl-note svg { flex: none; inline-size: var(--ds-size-icon-sm); block-size: var(--ds-size-icon-sm); }
.odl-ai-suggestion .odl-buttons { display: flex; flex-wrap: wrap; gap: var(--ds-space-inline-xs); }
.odl-ai-suggestion .odl-another { margin-inline-end: auto; }
.odl-ai-suggestion .odl-bar .odl-buttons { inline-size: 100%; }
.odl-ai-suggestion button { display: inline-flex; align-items: center; justify-content: 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); border: var(--ds-border-width-default) solid transparent; background: transparent; color: var(--ds-color-text-primary);
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-ai-suggestion button svg { flex: none; inline-size: var(--ds-size-icon-sm); block-size: var(--ds-size-icon-sm); }
.odl-ai-suggestion button:hover { background: var(--ds-color-bg-neutral-subtle); }
.odl-ai-suggestion .odl-accept { background: var(--ds-color-bg-action-primary); color: var(--ds-color-text-on-action); }
.odl-ai-suggestion .odl-accept:hover { background: var(--ds-color-bg-action-primary-hover); }
.odl-ai-suggestion .odl-accept:active { background: var(--ds-color-bg-action-primary-pressed); }
.odl-ai-suggestion .odl-reject { border-color: var(--ds-color-border-default); }
.odl-ai-suggestion button:focus-visible { outline: var(--ds-focus-ring-width) solid var(--ds-color-border-focus); outline-offset: var(--ds-focus-ring-offset); }
.odl-ai-suggestion .odl-done { display: flex; align-items: center; gap: var(--ds-space-inline-sm); margin: 0; color: var(--ds-color-text-secondary); font: var(--ds-text-body-sm); }
.odl-ai-suggestion .odl-done svg { flex: none; inline-size: var(--ds-size-icon-sm); block-size: var(--ds-size-icon-sm); color: var(--ds-color-text-success); }
.odl-ai-suggestion .odl-done button { margin-inline-start: auto; color: var(--ds-color-text-link); }
.odl-ai-suggestion[data-state="rejected"] .odl-done svg { color: var(--ds-color-icon-subtle); }
.odl-ai-suggestion [hidden] { display: none !important; }
@container (max-width: 26rem) { .odl-ai-suggestion .odl-buttons { inline-size: 100%; } .odl-ai-suggestion .odl-buttons button { flex: 1 1 auto; min-inline-size: 0; padding-inline: var(--ds-space-inset-xs); } .odl-ai-suggestion .odl-another { flex-basis: 100%; } }
@media (prefers-reduced-motion: reduce) { .odl-ai-suggestion, .odl-ai-suggestion *, .odl-ai-suggestion *::before { transition: none !important; animation: none !important; } }
</style>
<div class="odl-ai-suggestion" data-state="open">
<p class="odl-label" id="odl-ai-suggestion-label">Reply to Inchley Print Co.</p>
<div class="odl-field" role="group" aria-labelledby="odl-ai-suggestion-label">
<p>Hi Rhea, thanks for flagging the duplicate line on invoice INV-2207.</p>
<p><span class="odl-target" data-original="We will be sending the corrected invoice over to you at some point early next week, once finance has had a chance to look at it.">We will be sending the corrected invoice over to you at some point early next week, once finance has had a chance to look at it.</span> Nothing else on the account changes.</p>
</div>
<section class="odl-card" aria-label="Suggested edit">
<div class="odl-card-head"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><path d="M8 1.5 9.4 6.6 14.5 8 9.4 9.4 8 14.5 6.6 9.4 1.5 8 6.6 6.6z" stroke-linejoin="round"/></svg>Suggested edit<span class="odl-count" aria-live="polite">1 of 2</span></div>
<p class="odl-diff" data-text="We'll send the corrected invoice by Tue, Nov 3, after finance reviews it." data-why="Shorter and names the date"><del>We will be sending</del> <ins>We'll send</ins> the corrected invoice <del>over to you at some point early next week, once finance has had a chance to look at it.</del> <ins>by Tue, Nov 3, after finance reviews it.</ins></p>
<p class="odl-diff" hidden data-text="Finance is reviewing the corrected invoice now; you'll have it by Tue, Nov 3." data-why="Leads with what is happening and keeps the date"><del>We will be sending the corrected invoice over to you at some point early next week, once finance has had a chance to look at it.</del> <ins>Finance is reviewing the corrected invoice now; you'll have it by Tue, Nov 3.</ins></p>
<div class="odl-meta">
<p class="odl-why"><strong>Why:</strong> <span class="odl-reason">Shorter and names the date</span></p>
<p class="odl-note"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><rect x="3" y="2" width="10" height="12" rx="1.5"/><path d="M5.5 5.5h5M5.5 8h5M5.5 10.5h3"/></svg>Date from the billing calendar · high confidence</p>
</div>
<div class="odl-bar">
<div class="odl-buttons">
<button type="button" class="odl-another"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><path d="M13 5.5A5.5 5.5 0 0 0 3 6.5M3 2.5v4h4M3 10.5a5.5 5.5 0 0 0 10-1M13 13.5v-4H9"/></svg>Try another</button>
<button type="button" class="odl-reject">Reject</button>
<button type="button" class="odl-accept"><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>Accept</button>
</div>
</div>
</section>
<p class="odl-done" hidden role="status"><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 class="odl-done-text">Edit applied.</span><button type="button" class="odl-undo">Undo</button></p>
</div>
<script>
document.querySelectorAll(".odl-ai-suggestion").forEach((root) => {
if (root.dataset.odlReady) return; // wire once, even if the file is pasted twice
root.dataset.odlReady = "1";
const target = root.querySelector(".odl-target"), card = root.querySelector(".odl-card"), done = root.querySelector(".odl-done");
const diffs = [...root.querySelectorAll(".odl-diff")], reason = root.querySelector(".odl-reason"), count = root.querySelector(".odl-count");
let i = 0;
const show = (n) => { i = n; diffs.forEach((d, k) => { d.hidden = k !== i; }); reason.textContent = diffs[i].dataset.why; count.textContent = `${i + 1} of ${diffs.length}`; };
const finish = (state, text) => { root.dataset.state = state; card.hidden = true; done.hidden = false; root.querySelector(".odl-done-text").textContent = text; root.querySelector(".odl-undo").focus(); };
root.querySelector(".odl-another").addEventListener("click", () => show((i + 1) % diffs.length));
root.querySelector(".odl-accept").addEventListener("click", () => { target.textContent = diffs[i].dataset.text; finish("accepted", "Edit applied."); });
root.querySelector(".odl-reject").addEventListener("click", () => { target.textContent = target.dataset.original; finish("rejected", "Suggestion dismissed. Your sentence is unchanged."); });
root.querySelector(".odl-undo").addEventListener("click", () => { target.textContent = target.dataset.original; root.dataset.state = "open"; done.hidden = true; card.hidden = false; root.querySelector(".odl-accept").focus(); });
});
</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.
An AI agent's run as a timeline: what it has done, what it is doing now, the step waiting for your approval, what it skipped, with times, tool output and a way to stop it.
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.