独家
Intent Composer <mv-intent-composer>
AI 请求编辑器,在你输入时让意图的三个部分清晰可见:结果、约束(预算、截止日期、需要避免的事项、地点、数量…)以及委托边界。
工作原理
用于 AI 请求的输入器,在你输入时把意图的三个部分呈现出来:结果、约束(预算、截止时间、要避免的事项、地点、数量……)以及委托边界。识别出的片段会就地高亮,每个部分有各自的下划线样式,随后飞入三个槽位,成为可编辑、可移除的标签;空的槽位提供一键补充的提示,一个计量条显示意图的完整程度。一个三档委托牵引绳(Suggest · Prepare · Act)会根据你使用的动词自动设定(除非你自己选择),清楚说明助手可以做什么,并让提交按钮随之变形:处于 Act 档时,执行需要长按。解析器可插拔(同步或异步,例如一个 LLM);内置的启发式规则能理解英语和法语。
| 分类 | 表单 |
|---|---|
| 类型 | Web Component(<mv-intent-composer>) |
| 状态 | 稳定版 |
| 套件 | 可核查的 AI |
| 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
安装
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 & work assistant</h3><span>Alder & 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 | 类型 | 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 | 类型 | 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. 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.