익스클루시브
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/marvelousMarvelous 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.