独家

Intent Composer <mv-intent-composer>

AI 请求编辑器,在你输入时让意图的三个部分清晰可见:结果、约束(预算、截止日期、需要避免的事项、地点、数量…)以及委托边界。

工作原理

用于 AI 请求的输入器,在你输入时把意图的三个部分呈现出来:结果、约束(预算、截止时间、要避免的事项、地点、数量……)以及委托边界。识别出的片段会就地高亮,每个部分有各自的下划线样式,随后飞入三个槽位,成为可编辑、可移除的标签;空的槽位提供一键补充的提示,一个计量条显示意图的完整程度。一个三档委托牵引绳(Suggest · Prepare · Act)会根据你使用的动词自动设定(除非你自己选择),清楚说明助手可以做什么,并让提交按钮随之变形:处于 Act 档时,执行需要长按。解析器可插拔(同步或异步,例如一个 LLM);内置的启发式规则能理解英语和法语。

分类表单
类型Web Component(<mv-intent-composer>)
状态稳定版
套件可核查的 AI
Keywordsexclusive, 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

  • A conversational chat with streaming replies is the main experience → use AI Chat instead
  • The text is ordinary free input with no AI intent to structure → use Textarea instead
  • Requests are written in languages other than English or French without a custom parser

安装

node scripts/add.mjs intent-composer --out ./src/marvelous

使用 Marvelous UI MCP 服务器的 AI 智能体:install_components({ slugs: ["intent-composer"], target_dir: "<absolute path>/src/marvelous", framework: "react" })。

复制的文件(含依赖):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。

用法

快速开始,最小可运行标记:

<mv-intent-composer name="intent">Plan my trip to Chicago next Tuesday, budget $400 max, no flights</mv-intent-composer>

标准标记,可在此基础上通过属性、data-* 和 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 &amp; work assistant</h3><span>Alder &amp; Finch 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类型DefaultDescription
labelstringYour requestVisible label of the text area.
placeholderstringDescribe the result you want, your constraints, and what the assistant may do on its own…Placeholder of the text area.
namestringForm field name (the element is form-associated).
valuestringInitial request text. The element's text content is used when absent.
delegationsuggest | prepare | actsuggestLevel used when the text contains no delegation verb. A detected verb overrides it; a manual choice on the leash overrides both.
holdnumber (ms)1200Press-and-hold duration required to execute at the Act level (minimum 400).
debouncenumber (ms)280Pause in typing before the text is analysed again (highlights follow the edit immediately).
rowsnumber3Minimum height in lines; the text area grows with its content.
max-rowsnumber10Height in lines after which the text area scrolls.
submit-asjson | textjsonSubmitted form value: a JSON intent { text, outcome, constraints, delegation } or the plain text.
disabledbooleanDisables the field (also follows a disabled fieldset).
requiredbooleanThe request cannot be empty (form validation).
data-levelsuggest | prepare | actReflected: effective delegation level, for styling.
data-analyzingbooleanReflected while an asynchronous parser is running.

Properties

Name类型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.
valuestringRequest text (read / write).
intentobjectRead 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

NameDescription
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

NameDescription
mv-intent-changeAfter each analysis or delegation change. detail: the intent ({ text, outcome, constraints, delegation, completeness, complete }).
mv-intent-submitCancelable. detail: { text, outcome, constraints, delegation }. When not cancelled and the element is inside a form, the form is submitted (requestSubmit).

Content structure

NameDescription
(text)Initial request as plain text content (rendered before the element upgrades, which also suits SSR).

CSS classes

NameDescription
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-chipThe three slots, their chips (data-type, data-kind) and the augmentation prompts.
mv-intent-composer-track / -stop / -thumbDelegation leash (role=radiogroup).
mv-intent-composer-submit[data-level]Morphing submit button (data-holding, data-armed, data-state="sent").
mv-intent-composer-meterIntent completeness (role=meter, 3 segments: full, hatched = partial, empty).

CSS variables

NameDefaultDescription
--mv-intent-composer-outcomevar(--mv-accent)Outcome color (highlight, slot, meter).
--mv-intent-composer-constraintvar(--mv-info)Constraints color.
--mv-intent-composer-delegationvar(--mv-warning)Delegation verbs color.
--mv-intent-composer-actvar(--mv-warning)Emphasized edge and hold ring at the Act level.
--mv-intent-composer-font-sizevar(--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. Forced colors: chips, leash, meter and submit edges are redrawn with system colors, the chosen level is outlined in Highlight. Known limits: the built-in parser is heuristic (regular expressions, English then French); other languages or an LLM plug in through the parser property. A custom parser that returns no character offsets gives chips without highlights and without in-place editing. Ignoring a delegation verb ignores every occurrence of that verb in the text.

本页面由 AI 翻译。报告翻译问题