Canvas Design System
Main Site Tokens

Agent Proposal

A change the AI wants to make to something the person owns, shown in full before anything happens: what changes, from what to what, and why. Then Apply, an optional Adjust, and Not now, which is always there.

When to Use

Use when: The AI proposes a change to the person's agenda, recap or live session and the person decides whether it happens. Show every item that changes, before and after, and the reason in their terms. The facilitator is the expert; the proposal is a second set of eyes they can turn down in one click, and undo in one more.
Don't use when: The AI is recommending one library item to add; that is the Suggestion Card. A change the person asked for and already confirmed needs no second proposal, only a Toast.

Variants

A timed change, with Adjust

Each change is a label with an optional at (a time or a position), and was and new. A change with only new is an addition. Try the buttons: Apply hides the card and offers Undo in a toast, Not now removes it, and the line under the card shows each event.

Suggested change

Still end at 11:00, and keep the full decision

  • 9:55 Break was 15m , now 5m
  • 10:12 Restate the question now New, 3m
  • 10:45 Commit: owners and first steps was 20m , now 15m

Why: You are 12 minutes behind, and three people asked which question they are answering. Restating it costs 3 minutes; the break and the close pay for it.

No event yet.

<?= ws_agent_proposal([
    'id'      => 'adapt-' . $proposalId,
    'title'   => 'Still end at 11:00, and keep the full decision',
    'changes' => [
        ['at' => '9:55',  'label' => 'Break', 'was' => '15m', 'new' => '5m'],
        ['at' => '10:12', 'label' => 'Restate the question', 'new' => 'New, 3m'],
    ],
    'why'     => 'You are 12 minutes behind, and three people asked which question they are answering.',
    'apply'   => 'Apply to the session',
    'adjust'  => 'Adjust',
    'toast'   => 'Applied. The session still ends at 11:00.',
]) ?>
<?= ws_toast_container() ?>
<script src="/js/ws-agent.js?v=<?= filemtime(WS_ROOT . '/js/ws-agent.js') ?>"></script>

Removed items, no Adjust

'removed' => true strikes the item through and says Removed in words. Without an adjust label there is no Adjust button; Not now is always there.

Suggested change

Make room for the decision

  • Lightning Demos Removed
  • Dot voting was 10m , now 25m

Why: The decision needs about 24 minutes for 12 people choosing 3 of 12.

From JavaScript, and saving before you confirm

WsAgent.proposal(proposal) prints the same markup, so a chat reply can draw one. When Apply has to save first, prevent the event, save, then call WsAgent.applied(card), which hides the card and offers Undo.

thread.insertAdjacentHTML('beforeend', WsAgent.proposal({ id: 'chat-7', title: 'Tighten the recap', changes, why }));

document.addEventListener('ws:proposal-apply', async (event) => {
    event.preventDefault();                       // the toast waits for the save
    await saveChange(event.detail.id);
    WsAgent.applied(event.detail.proposal);
});
document.addEventListener('ws:proposal-undo', (event) => revertChange(event.detail.id));
document.addEventListener('ws:proposal-dismiss', (event) => logDismissed(event.detail.id));
document.addEventListener('ws:proposal-adjust', (event) => openEditor(event.detail.id));

States

StateBehavior
ProposedThe card, its changes, the reason and the actions.
Appliedws:proposal-apply ran and nothing prevented it: the card is hidden, carries data-ws-proposal-state="applied", and a success toast says so with Undo (unless 'undo' => false). It stays in the page so Undo can bring it back.
UndoneUndo dispatches ws:proposal-undo; unless prevented, the card comes back and its Apply button takes focus.
DismissedNot now dispatches ws:proposal-dismiss; unless prevented, the card is removed.
AdjustingAdjust dispatches ws:proposal-adjust. The card stays; the page opens its own editor.

Real-World Usage

Synthesize's "Draft a change" and Agenda Check's "Fix" move onto it; Synthesize gains the Not now it never had. Facilitate's Help me adapt proposes changes to the rest of a live session the same way.

Options

OptionTypeDefaultPurpose
idstring'ws-agent-proposal'Required in practice: unique on the page. The title takes {id}-title; events carry it as detail.id.
kickerstring'Suggested change'The line above the title
titlestring''What the change achieves, in a sentence
changesarray[]Each: label, at, was, new, removed
whystring''The reason, in the person's terms
apply / adjust / dismissstring'Apply' / '' / 'Not now'Button text. No Adjust without its text.
toaststring'Change applied.'The toast after Apply
undobooltrueOffer Undo in that toast. Turn it off only when the change truly cannot be reversed.
returnFocusstring''A selector for where focus goes when the card leaves
class / attrsstring / arrayAdditional classes and attributes

Markup contract: the section carries data-ws-proposal="{id}" and data-ws-proposal-toast; the buttons carry data-ws-proposal-action (apply | adjust | dismiss). The page needs ws_toast_container(), components.js and /js/ws-agent.js.

Accessibility

ConcernBehavior
RegionA <section> named by its title through aria-labelledby, so a screen reader can find it among the page's regions; the title is an h3.
Before and afterStrikethrough is not read aloud, so each change carries visually hidden words: "was 15m, now 5m". A removed item says Removed in visible text.
ActionsApply, Adjust and Not now are real buttons, in that order, so Tab reaches the primary action first.
Focus when the card leavesFocus moves to returnFocus when set, else to the next proposal beside it, else the previous one, else to the card's container. Undo brings the card back and focuses its Apply button.
The toastThe success toast is announced by the toast container, and it pauses while hovered or focused, so there is time to reach Undo.

Tokens

TokenUsed for
--brandThe 2px border that marks a decision waiting
--brand-tint / --brand-line / --brand-inkThe head, its rule, the kicker and the new values
--font-heading / --text-h4The title
--card-bgThe actions row
--radius-xl / --shadow-smShape and lift, matched to the cards around it

CSS Classes

ClassPurpose
.ws-agent-proposalThe section
.ws-agent-proposal__head / __kicker / __titleThe head
.ws-agent-proposal__changes / __changeThe list of changes; __change--removed for a removed item
.ws-agent-proposal__at / __what / __delta / __was / __newThe parts of one change
.ws-agent-proposal__why / __why-labelThe reason
.ws-agent-proposal__actionsThe buttons

Files

FilePurpose
includes/components/helpers.phpws_agent_proposal() helper
includes/components/agent-proposal.phpTemplate
includes/components/components.cssStyles, in the agent components section
js/ws-agent.jsWsAgent.proposal(), WsAgent.applied() and the events