Exclusivobeta
Intent Composer <mv-intent-composer>
Un editor de peticiones a la IA que hace visibles las tres partes de una intención mientras escribes: el resultado, las restricciones (presupuesto, plazo, cosas que evitar, lugar, cantidad…) y el límite de delegación. Los fragmentos detectados se resaltan en su sitio, cada parte con su propio estilo de subrayado, y luego vuelan a tres casillas como chips editables y eliminables; las casillas vacías ofrecen sugerencias para completarlas con un clic y un medidor muestra lo completa que está la intención. Una correa de delegación de tres posiciones (Suggest · Prepare · Act) se ajusta según los verbos que usas, salvo que la elijas tú, explica lo que el asistente puede hacer y transforma el botón de envío: en Act, ejecutar requiere mantenerlo pulsado. Analizador intercambiable (síncrono o asíncrono, por ejemplo un LLM); las heurísticas integradas entienden inglés y francés.
| Categoría | Formularios |
|---|---|
| Tipo | Web Component (<mv-intent-composer>) |
| Estado | beta |
| Kit | IA que puedes verificar |
| Keywords | exclusive, ai, prompt, intent, composer, llm, agent, delegation, autonomy, prompt-augmentation, constraints, highlight, textarea, hold-to-confirm, form |
When to use
- Users write requests for an AI assistant and should see outcome, constraints and delegation level
- An agent may take real actions, so executing must be clearly bounded and confirmed by holding
- Prompts should be enriched by nudging users to add missing constraints like budget or deadline
Avoid when
Instalación
node scripts/add.mjs intent-composer --out ./src/marvelousAgente de IA con el servidor MCP de Marvelous UI: install_components({ slugs: ["intent-composer"], target_dir: "<absolute path>/src/marvelous", framework: "react" }).
Archivos copiados (dependencias incluidas): tokens/tokens.css, core/base.css, core/dom.js, core/element.js, core/focus.js, core/form.js, core/motion.js, core/observe.js, components/intent-composer/intent-composer.js, components/intent-composer/intent-composer.css.
Uso
Inicio rápido, el marcado mínimo que funciona:
<mv-intent-composer name="intent">Plan my trip to Chicago next Tuesday, budget $400 max, no flights</mv-intent-composer>Marcado de referencia: parte de él y personalízalo con atributos, data-* y variables CSS:
<div id="ic-demo" style="width:min(100%,54rem);margin-inline:auto">
<style>
#ic-demo .ic-title { display:flex; align-items:baseline; justify-content:space-between; flex-wrap:wrap; gap:.25rem 1rem; margin:0 0 .75rem }
#ic-demo .ic-title h3 { margin:0; font-size:1.125rem; letter-spacing:-.01em }
#ic-demo .ic-title span { color:var(--mv-fg-muted); font-size:.8125rem }
#ic-demo .ic-card { padding:1.125rem 1.25rem 1.25rem; border:1px solid var(--mv-border); border-radius:var(--mv-radius-xl); background:var(--mv-surface); box-shadow:var(--mv-shadow-sm) }
#ic-demo .ic-try { display:flex; align-items:center; flex-wrap:wrap; gap:.5rem; margin-top:.875rem }
#ic-demo .ic-try > span { color:var(--mv-fg-muted); font-size:.8125rem; margin-inline-end:.125rem }
#ic-demo .ic-log { min-height:1.25rem; margin:.75rem 0 0; color:var(--mv-fg-muted); font-size:.8125rem; text-align:center }
#ic-demo .ic-log strong { color:var(--mv-fg); font-weight:600 }
#ic-demo details { margin-top:.5rem; font-size:.8125rem }
#ic-demo summary { cursor:pointer; color:var(--mv-fg-muted); width:max-content }
#ic-demo pre { max-height:14rem; overflow:auto; margin:.5rem 0 0; padding:.75rem 1rem; border-radius:var(--mv-radius-md); background:var(--mv-bg-muted); color:var(--mv-fg); font:.75rem/1.5 var(--mv-font-mono) }
@media (max-width:30rem) { #ic-demo .ic-card { padding:1rem .875rem } }
</style>
<div class="ic-title"><h3>Travel & work assistant</h3><span>Northwind Consulting · Business travel</span></div>
<div class="ic-card">
<mv-intent-composer id="ic-composer" name="intent" label="What should the assistant do?" hold="1400">Plan my trip to Chicago next Tuesday, budget $400 max, no flights, and book the hotel near Union Station</mv-intent-composer>
</div>
<div class="ic-try" role="group" aria-label="Example requests">
<span>Try:</span>
<button class="mv-button" data-variant="outline" data-size="sm" type="button" data-example="Plan my trip to Chicago next Tuesday, budget $400 max, no flights, and book the hotel near Union Station">Chicago trip</button>
<button class="mv-button" data-variant="outline" data-size="sm" type="button" data-example="Find me three gift ideas for my sister, under $50, delivered by Saturday">Gift ideas</button>
<button class="mv-button" data-variant="outline" data-size="sm" type="button" data-example="Draft an email to the team to move Thursday's meeting, friendly tone, without mentioning the budget">Team email</button>
<button class="mv-button" data-variant="outline" data-size="sm" type="button" data-example="Plan my Chicago trip">Underspecified</button>
</div>
<p class="ic-log" id="ic-log" role="status"></p>
<details>
<summary>Structured intent (JSON)</summary>
<pre id="ic-json"></pre>
</details>
<script type="module">
const c = document.getElementById("ic-composer");
await customElements.whenDefined("mv-intent-composer");
const out = document.getElementById("ic-json");
const log = document.getElementById("ic-log");
const show = () => {
const { text, ...rest } = c.intent;
out.textContent = JSON.stringify(rest, null, 2);
};
c.addEventListener("mv-intent-change", show);
show();
c.addEventListener("mv-intent-submit", (e) => {
const { delegation, constraints, outcome } = e.detail;
log.replaceChildren(
Object.assign(document.createElement("strong"), { textContent: `${delegation.label}` }),
` · “${outcome || "No outcome"}” with ${constraints.length} constraint${constraints.length === 1 ? "" : "s"} sent to the assistant.`,
);
});
for (const b of document.querySelectorAll("#ic-demo [data-example]")) {
b.addEventListener("click", () => {
c.resetDelegation();
c.value = b.dataset.example;
log.textContent = "";
});
}
</script>
</div>API
Attributes
| Name | Tipo | Default | Description |
|---|---|---|---|
label | string | Your request | Visible label of the text area. |
placeholder | string | Describe the result you want, your constraints, and what the assistant may do on its own… | Placeholder of the text area. |
name | string | Form field name (the element is form-associated). | |
value | string | Initial request text. The element's text content is used when absent. | |
delegation | suggest | prepare | act | suggest | Level used when the text contains no delegation verb. A detected verb overrides it; a manual choice on the leash overrides both. |
hold | number (ms) | 1200 | Press-and-hold duration required to execute at the Act level (minimum 400). |
debounce | number (ms) | 280 | Pause in typing before the text is analysed again (highlights follow the edit immediately). |
rows | number | 3 | Minimum height in lines; the text area grows with its content. |
max-rows | number | 10 | Height in lines after which the text area scrolls. |
submit-as | json | text | json | Submitted form value: a JSON intent { text, outcome, constraints, delegation } or the plain text. |
disabled | boolean | Disables the field (also follows a disabled fieldset). | |
required | boolean | The request cannot be empty (form validation). | |
data-level | suggest | prepare | act | Reflected: effective delegation level, for styling. | |
data-analyzing | boolean | Reflected while an asynchronous parser is running. |
Properties
| Name | Tipo | Description |
|---|---|---|
parser | (text) => IntentResult | Promise<IntentResult> | Replaces the built-in heuristics (can be set before the element is defined). IntentResult: { outcome: string | { text, start?, end?, spans?: [start, end][], vague? }, constraints: { type, text, label?, start?, end? }[], delegation: "suggest" | "prepare" | "act" | { level, verbs?: { level, text, phrase?, start?, end? }[] }, spans?: { type: "outcome" | "constraint" | "delegation", kind?, start, end }[] }. Offsets are optional: without them there is no highlight or chip editing. Stale async results are dropped. |
value | string | Request text (read / write). |
intent | object | Read only: { text, outcome, constraints, delegation: { level, label, mode: "auto" | "manual", verbs }, completeness (0-3), complete }. |
level | "suggest" | "prepare" | "act" | Effective delegation level. Assigning it is a manual choice; assigning null returns to automatic. |
Methods
| Name | Description |
|---|---|
analyze() | Runs the parser now; resolves with the intent. |
insert(template) | Inserts text at the caret; a part wrapped in {braces} is selected afterwards so the user can type over it. |
resetDelegation() | Drops the manual choice: the text decides the level again. |
submit() | Submits programmatically (no hold required). Resolves to false if cancelled or empty. |
focus() | Focuses the text area. |
Events
| Name | Description |
|---|---|
mv-intent-change | After each analysis or delegation change. detail: the intent ({ text, outcome, constraints, delegation, completeness, complete }). |
mv-intent-submit | Cancelable. detail: { text, outcome, constraints, delegation }. When not cancelled and the element is inside a form, the form is submitted (requestSubmit). |
Content structure
| Name | Description |
|---|---|
(text) | Initial request as plain text content (rendered before the element upgrades, which also suits SSR). |
CSS classes
| Name | Description |
|---|---|
mv-intent-composer-mark[data-type] | Highlight behind the text: outcome (solid underline), constraint (dashed + tint), delegation (dotted + stronger tint), ignored verb (struck through). |
mv-intent-composer-slot / -chip / -hint-chip | The three slots, their chips (data-type, data-kind) and the augmentation prompts. |
mv-intent-composer-track / -stop / -thumb | Delegation leash (role=radiogroup). |
mv-intent-composer-submit[data-level] | Morphing submit button (data-holding, data-armed, data-state="sent"). |
mv-intent-composer-meter | Intent completeness (role=meter, 3 segments: full, hatched = partial, empty). |
CSS variables
| Name | Default | Description |
|---|---|---|
--mv-intent-composer-outcome | var(--mv-accent) | Outcome color (highlight, slot, meter). |
--mv-intent-composer-constraint | var(--mv-info) | Constraints color. |
--mv-intent-composer-delegation | var(--mv-warning) | Delegation verbs color. |
--mv-intent-composer-act | var(--mv-warning) | Emphasized edge and hold ring at the Act level. |
--mv-intent-composer-font-size | var(--mv-text-base) | Text size of the request (16px avoids zoom on iOS). |
Accessibility
The highlight layer is aria-hidden; the text area is a native textarea with a visible label and its keyboard hint as description. Each slot is a labelled group with a labelled list; chips are real buttons (Enter or F2 edits in place, Delete removes a constraint, Escape cancels an edit), removals go through the editing pipeline so Ctrl+Z in the text restores them. Ignoring a delegation verb strikes it through (never color alone). A polite live region announces detected parts in batches (“Constraint detected: budget, $400 max”), level changes and the boundary sentence, without interrupting typing. The leash is a role=radiogroup with roving focus (arrows, Home, End) and a described boundary; its mode (auto / manual) is shown as text. The completeness indicator is a role=meter with a spoken summary. At Act, a pointer press-and-hold fills a progress ring; on keyboard, holding Enter or Space works too, and a short press arms a confirm step (Enter again to execute, Escape to cancel); assistive-technology clicks use the same confirm step. Ctrl/⌘ Enter submits from the text (or arms confirmation at Act). Status colors always come with an icon and a label. Reduced motion: no flying chips or label morph; the hold ring still shows progress. No animation loop runs except while holding.