Skip to Content
Components

Permission gate

The agent asks before it touches something new.

The agent asks before it acts: one card with the question, the scopes to tick, and a footer with how long the grant lasts (durations) and the two answers. Once allowed it gives way to a shorter result row — a spinner while the agent is working, a green check when done — with a Revoke button; declining shows an undoable notice. A follow-up ask mid-run (followUp) turns the icon tile amber.

Preview
Allow the agent to edit Figma?It needs this to apply the pricing fixes

Usage

<!-- include core.css + permission-gate.css -->
<div data-c="permission-gate">
  <div class="pgx panel">
    <div class="pgx-h">
      <span class="pgx-ic"><!-- icon --></span>
      <div class="pgx-ht"><b>Allow the agent to edit Figma?</b><span class="muted">It needs this to apply the pricing fixes</span></div>
    </div>
    <div class="pgx-sc">
      <label class="pgx-o"><input type="checkbox" checked><span class="bx"><!-- check --></span><span class="t">Read frames<span class="d">Pricing, Checkout</span></span></label>
      <!-- more scopes -->
    </div>
    <div class="pgx-f">
      <button class="pgx-db" aria-haspopup="menu"><span class="pgx-dv">For this task</span><!-- chev --></button>
      <span class="sp"></span>
      <button class="pill ghost sm pgx-no">Not now</button>
      <button class="pill primary sm pgx-ok">Allow 2 scopes</button>
    </div>
  </div>
</div>

Properties

NameTypeDescription
titleReactNodeThe question, like "Allow the agent to edit Figma?".
subtitleReactNodeWhy it asks.
iconReactNodeThe glyph in the tile beside the question. Defaults to a shield.
scopesPermissionScope[]The things to allow: { key, label, detail?, risk?, riskDetail? }.
value / defaultValuestring[]Keys of the ticked scopes. Leave value out to let the checkboxes hold their own.
onValueChange(keys) => voidFires when the ticked scopes change.
durationsstring[]How long the grant lasts, in the footer dropdown. Defaults to This task, Today, Always.
durationDetailsRecord<string, string>The quiet line under each duration in its menu.
duration / defaultDurationstringThe picked duration.
status / defaultStatusasking | allowed | declinedWhich view shows. Leave status out to let the buttons move it.
onAllow(keys, duration) => void | PromiseCalled on Allow; the button spins until a returned promise settles.
onDecline / onRevoke / onUndo() => voidCalled by the decline, Revoke and Undo buttons.
allowedTitle / allowedDetailstringThe two lines on the allowed row, like "Done. 6 frames edited".
workingbooleanThe agent is still working: a spinner on a neutral row instead of the green check.
metaReactNodeQuiet text at the end of the allowed row, like an elapsed time "0:04".
followUpbooleanA follow-up ask mid-run, for one more scope: the icon tile turns amber.
declineLabelstringThe decline button. Defaults to "Not now".
declinedTitle / declinedDetailReactNodeShown after declining.
barebooleanSits flat in a chat thread (the default). Set false to give it a card of its own.

Theming

Works in light and dark (data-theme="dark") and in the Soft style (data-style="soft"). The scopes, footer and result row all draw from the shared --dm-* tokens: the ticked box and primary button take the accent, the risk tag and the follow-up tile the warn tint, and the allowed row the ok tint; in Soft the card, pills and tick boxes are rounder.

On this page