@aparte/plugin-ask-user 0.19.2 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -526,7 +526,7 @@
|
|
|
526
526
|
"type": {
|
|
527
527
|
"text": "AparteSegmentRenderer<QuestionReceiptSegment>"
|
|
528
528
|
},
|
|
529
|
-
"default": "{ type: 'question-receipt', render(seg) { // The card wears core's mark (display/mark.css) — see `buildReceipt`, the DOM // path, for why an answer is success and a decline is quiet. if (seg.declined) { return `<div class=\"aparte-segment aparte-tag aparte-question-receipt aparte-mark aparte-mark--quiet aparte-question-receipt--declined\" data-segment-id=\"${esc(seg.id)}\"> <span class=\"aparte-tag__label aparte-question-receipt__answer--declined\">${esc(seg.answer)}</span> </div>`; } return `<div class=\"aparte-segment aparte-tag aparte-question-receipt aparte-mark aparte-mark--success\" data-segment-id=\"${esc(seg.id)}\"> <span class=\"aparte-tag__label aparte-question-receipt__question\">${esc(seg.question)}</span> <span class=\"aparte-question-receipt__sep\">→</span> <span class=\"aparte-tag__label aparte-question-receipt__answer\">${esc(seg.answer)}</span> </div>`; }, update(el, seg) { const q = el.querySelector('.aparte-question-receipt__question'); if (q) q.textContent = seg.question; const a = el.querySelector('.aparte-question-receipt__answer'); if (a) a.textContent = seg.answer; }, getStyles() { return ` /* ── Question Receipt Card ──────────────────────────────────────────────── */ /* Several questions leave several cards, stacked. The tool renderer builds this group; the card below is shared with a question-receipt segment an app emits itself. (No backticks in here: this whole block is a template literal.) THE CARD IS AN .aparte-tag. It is a pill holding a truncating label, which is what that recipe is, and it used to redeclare the whole thing: inline-flex, gap, padding, border, radius, surface background, max-width — nine lines that core already owned. What is left below is only what a tag has no opinion about (the entrance, and the share of the width each half gets) plus this card's own measures, expressed as the tag's tokens so they land ON the recipe rather than beside it. This is also the only place in the repo where a PLUGIN reaches core's recipes, and that is the point: the recipes are plain classes on a stylesheet core already ships, so a plugin needs no import, no client, and no build step to use them. A capability that only core itself can reach would not be one. */ /* A declined request: the outcome, in the muted voice of something that did not happen — not the green of an answer given. */ .aparte-question-receipt__answer--declined { color: var(--aparte-text-muted); font-style: italic; } .aparte-question-receipt__group { display: flex; flex-direction: column; align-items: flex-start; gap: var(--aparte-space-2); } .aparte-question-receipt { --aparte-tag-gap: var(--aparte-space-4); --aparte-tag-padding: var(--aparte-space-3) var(--aparte-space-6); --aparte-tag-radius: var(--aparte-radius-full); --aparte-tag-font-size: var(--aparte-font-size-md); overflow: hidden; animation: aparte-question-receipt-appear var(--aparte-duration-slow) ease-out both; } /* The question yields the room, the answer keeps it: an answer cut in half is a receipt that records nothing, and the question is usually the re-readable half. */ .aparte-question-receipt__question { color: var(--aparte-text-muted); flex-shrink: 1; min-width: 0; } .aparte-question-receipt__sep { color: var(--aparte-text-muted); opacity: 0.4; flex-shrink: 0; } .aparte-question-receipt__answer { /* The strong text colour, not the success green: the answer is a fact of the transcript, not a verdict, and green was the one hue outside the palette on the whole page (UI audit 2026-08-28, §8.3 #4). The weight does the emphasis. */ color: var(--aparte-text); font-weight: var(--aparte-font-weight-semibold); flex-shrink: 0; max-width: 55%; } @keyframes aparte-question-receipt-appear { from { opacity: 0; transform: translateY(var(--aparte-space-2)); } to { opacity: 1; transform: translateY(0); } } `; }, }"
|
|
529
|
+
"default": "{ type: 'question-receipt', render(seg) { // The card wears core's mark (display/mark.css) — see `buildReceipt`, the DOM // path, for why an answer is success and a decline is quiet. if (seg.declined) { return `<div class=\"aparte-segment aparte-tag aparte-question-receipt aparte-mark aparte-mark--quiet aparte-question-receipt--declined\" data-segment-id=\"${esc(seg.id)}\"> <span class=\"aparte-tag__label aparte-question-receipt__answer--declined\">${esc(seg.answer)}</span> </div>`; } return `<div class=\"aparte-segment aparte-tag aparte-question-receipt aparte-mark aparte-mark--success\" data-segment-id=\"${esc(seg.id)}\"> <span class=\"aparte-tag__label aparte-question-receipt__question\">${esc(seg.question)}</span> <span class=\"aparte-question-receipt__sep\">→</span> <span class=\"aparte-tag__label aparte-question-receipt__answer\">${esc(seg.answer)}</span> </div>`; }, update(el, seg) { const q = el.querySelector('.aparte-question-receipt__question'); if (q) q.textContent = seg.question; const a = el.querySelector('.aparte-question-receipt__answer'); if (a) a.textContent = seg.answer; }, getStyles() { return ` /* ── Question Receipt Card ──────────────────────────────────────────────── */ /* Several questions leave several cards, stacked. The tool renderer builds this group; the card below is shared with a question-receipt segment an app emits itself. (No backticks in here: this whole block is a template literal.) THE CARD IS AN .aparte-tag. It is a pill holding a truncating label, which is what that recipe is, and it used to redeclare the whole thing: inline-flex, gap, padding, border, radius, surface background, max-width — nine lines that core already owned. What is left below is only what a tag has no opinion about (the entrance, and the share of the width each half gets) plus this card's own measures, expressed as the tag's tokens so they land ON the recipe rather than beside it. This is also the only place in the repo where a PLUGIN reaches core's recipes, and that is the point: the recipes are plain classes on a stylesheet core already ships, so a plugin needs no import, no client, and no build step to use them. A capability that only core itself can reach would not be one. */ /* A declined request: the outcome, in the muted voice of something that did not happen — not the green of an answer given. */ .aparte-question-receipt__answer--declined { color: var(--aparte-text-muted); font-style: italic; } .aparte-question-receipt__group { display: flex; flex-direction: column; align-items: flex-start; gap: var(--aparte-space-2); } .aparte-question-receipt { --aparte-tag-gap: var(--aparte-space-4); --aparte-tag-padding: var(--aparte-space-3) var(--aparte-space-6); --aparte-tag-radius: var(--aparte-radius-full); --aparte-tag-font-size: var(--aparte-font-size-md); overflow: hidden; animation: aparte-question-receipt-appear var(--aparte-duration-slow) ease-out both; } /* The question yields the room, the answer keeps it: an answer cut in half is a receipt that records nothing, and the question is usually the re-readable half. */ /* The neutral as text, not the muted token: muted on the success mark is 4.29:1. */ .aparte-question-receipt__question { color: var(--aparte-neutral-ink, var(--aparte-text-muted)); flex-shrink: 1; min-width: 0; } .aparte-question-receipt__sep { color: var(--aparte-text-muted); opacity: 0.4; flex-shrink: 0; } .aparte-question-receipt__answer { /* The strong text colour, not the success green: the answer is a fact of the transcript, not a verdict, and green was the one hue outside the palette on the whole page (UI audit 2026-08-28, §8.3 #4). The weight does the emphasis. */ color: var(--aparte-text); font-weight: var(--aparte-font-weight-semibold); flex-shrink: 0; max-width: 55%; } @keyframes aparte-question-receipt-appear { from { opacity: 0; transform: translateY(var(--aparte-space-2)); } to { opacity: 1; transform: translateY(0); } } `; }, }"
|
|
530
530
|
}
|
|
531
531
|
],
|
|
532
532
|
"exports": [
|
package/dist/index.js
CHANGED
|
@@ -62,8 +62,9 @@ const questionReceiptRenderer = {
|
|
|
62
62
|
}
|
|
63
63
|
/* The question yields the room, the answer keeps it: an answer cut in half is a
|
|
64
64
|
receipt that records nothing, and the question is usually the re-readable half. */
|
|
65
|
+
/* The neutral as text, not the muted token: muted on the success mark is 4.29:1. */
|
|
65
66
|
.aparte-question-receipt__question {
|
|
66
|
-
color: var(--aparte-text-muted);
|
|
67
|
+
color: var(--aparte-neutral-ink, var(--aparte-text-muted));
|
|
67
68
|
flex-shrink: 1;
|
|
68
69
|
min-width: 0;
|
|
69
70
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/question-receipt.renderer.ts","../src/aparte-ask-user.ts","../src/index.ts"],"sourcesContent":["import type { AparteSegmentRenderer } from '@aparte/core';\nimport { escapeAttr } from '@aparte/core';\n\nexport interface QuestionReceiptSegment {\n id: string;\n type: 'question-receipt';\n isStreaming?: boolean;\n question: string;\n answer: string;\n /**\n * The user declined the request: `answer` is then the outcome sentence, not an\n * answer to `question`, and the card shows it alone in the mark's quiet voice.\n * The tool renderer's DOM path had this branch; an app emitting the segment\n * itself had no way to say it.\n */\n declined?: boolean;\n}\n\n// Core owns the escaping; this alias keeps the call sites short. The inlined\n// copy that used to live here left the apostrophe through.\nconst esc = escapeAttr;\n\nexport const questionReceiptRenderer: AparteSegmentRenderer<QuestionReceiptSegment> = {\n type: 'question-receipt',\n\n render(seg) {\n // The card wears core's mark (display/mark.css) — see `buildReceipt`, the DOM\n // path, for why an answer is success and a decline is quiet.\n if (seg.declined) {\n return `<div class=\"aparte-segment aparte-tag aparte-question-receipt aparte-mark aparte-mark--quiet aparte-question-receipt--declined\" data-segment-id=\"${esc(seg.id)}\">\n <span class=\"aparte-tag__label aparte-question-receipt__answer--declined\">${esc(seg.answer)}</span>\n</div>`;\n }\n return `<div class=\"aparte-segment aparte-tag aparte-question-receipt aparte-mark aparte-mark--success\" data-segment-id=\"${esc(seg.id)}\">\n <span class=\"aparte-tag__label aparte-question-receipt__question\">${esc(seg.question)}</span>\n <span class=\"aparte-question-receipt__sep\">→</span>\n <span class=\"aparte-tag__label aparte-question-receipt__answer\">${esc(seg.answer)}</span>\n</div>`;\n },\n\n update(el, seg) {\n const q = el.querySelector('.aparte-question-receipt__question');\n if (q) q.textContent = seg.question;\n const a = el.querySelector('.aparte-question-receipt__answer');\n if (a) a.textContent = seg.answer;\n },\n\n getStyles() {\n return `\n/* ── Question Receipt Card ──────────────────────────────────────────────── */\n/* Several questions leave several cards, stacked. The tool renderer builds this\n group; the card below is shared with a question-receipt segment an app emits\n itself. (No backticks in here: this whole block is a template literal.)\n\n THE CARD IS AN .aparte-tag. It is a pill holding a truncating label, which is what\n that recipe is, and it used to redeclare the whole thing: inline-flex, gap, padding,\n border, radius, surface background, max-width — nine lines that core already owned.\n What is left below is only what a tag has no opinion about (the entrance, and the\n share of the width each half gets) plus this card's own measures, expressed as the\n tag's tokens so they land ON the recipe rather than beside it.\n\n This is also the only place in the repo where a PLUGIN reaches core's recipes, and\n that is the point: the recipes are plain classes on a stylesheet core already ships,\n so a plugin needs no import, no client, and no build step to use them. A capability\n that only core itself can reach would not be one. */\n/* A declined request: the outcome, in the muted voice of something that did not\n happen — not the green of an answer given. */\n.aparte-question-receipt__answer--declined {\n color: var(--aparte-text-muted);\n font-style: italic;\n}\n.aparte-question-receipt__group {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--aparte-space-2);\n}\n.aparte-question-receipt {\n --aparte-tag-gap: var(--aparte-space-4);\n --aparte-tag-padding: var(--aparte-space-3) var(--aparte-space-6);\n --aparte-tag-radius: var(--aparte-radius-full);\n --aparte-tag-font-size: var(--aparte-font-size-md);\n overflow: hidden;\n animation: aparte-question-receipt-appear var(--aparte-duration-slow) ease-out both;\n}\n/* The question yields the room, the answer keeps it: an answer cut in half is a\n receipt that records nothing, and the question is usually the re-readable half. */\n.aparte-question-receipt__question {\n color: var(--aparte-text-muted);\n flex-shrink: 1;\n min-width: 0;\n}\n.aparte-question-receipt__sep {\n color: var(--aparte-text-muted);\n opacity: 0.4;\n flex-shrink: 0;\n}\n.aparte-question-receipt__answer {\n /* The strong text colour, not the success green: the answer is a fact of the\n transcript, not a verdict, and green was the one hue outside the palette on the\n whole page (UI audit 2026-08-28, §8.3 #4). The weight does the emphasis. */\n color: var(--aparte-text);\n font-weight: var(--aparte-font-weight-semibold);\n flex-shrink: 0;\n max-width: 55%;\n}\n@keyframes aparte-question-receipt-appear {\n from { opacity: 0; transform: translateY(var(--aparte-space-2)); }\n to { opacity: 1; transform: translateY(0); }\n}\n`;\n },\n};\n","/**\n * <aparte-ask-user> — a semantic alias of core's <aparte-elicitation>.\n *\n * ask_user runs on the core elicitation primitive; it has no bespoke Web\n * Component. This thin subclass lets you mount the presenter with intent-revealing\n * markup (`<aparte-ask-user>`) instead of the generic `<aparte-elicitation>`.\n * The two are interchangeable.\n */\n\nimport { AparteElicitation } from '@aparte/core';\n\n/**\n * A semantic alias of core's `<aparte-elicitation>`: same presenter, intent-revealing\n * tag. `ask_user` runs on the core elicitation primitive and has no bespoke component,\n * so this subclass adds no behaviour — it exists so markup can say what it means. The\n * two are interchangeable, and neither dispatches anything.\n *\n * @element aparte-ask-user\n *\n * @example\n * <!-- Identical to <aparte-elicitation>; mount either one, never both. -->\n * <aparte-chat>\n * <aparte-chat-viewport></aparte-chat-viewport>\n * <aparte-ask-user></aparte-ask-user>\n * <aparte-composer></aparte-composer>\n * </aparte-chat>\n */\nexport class AparteAskUser extends AparteElicitation {}\n\nif (typeof customElements !== 'undefined' && !customElements.get('aparte-ask-user')) {\n customElements.define('aparte-ask-user', AparteAskUser);\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'aparte-ask-user': AparteAskUser;\n }\n}\n","/**\n * @aparte/plugin-ask-user\n *\n * The built-in `ask_user` tool — a thin adapter over the core elicitation\n * primitive. The AI asks the user a structured choice; the handler forwards it to\n * `requestUserInput`, presented by `<aparte-elicitation>` (or the semantic\n * `<aparte-ask-user>` alias registered by importing this package).\n *\n * Usage:\n * import { setupAskUser } from '@aparte/plugin-ask-user';\n * setupAskUser(); // registers the tool + hides its bubble segment\n * // mount <aparte-elicitation> (or <aparte-ask-user>) in your chat\n */\n\nimport { aparteGlobalConfig, registerSegmentRenderer, type AparteConfig } from '@aparte/core';\nimport { createAskUserTool, askUserHandler, type AskUserSetupOptions } from './ask-user.js';\nimport { questionReceiptRenderer } from './question-receipt.renderer.js';\nimport { buildReceipt } from './receipt.js';\n\n// Register the <aparte-ask-user> semantic alias (subclass of <aparte-elicitation>).\nimport './aparte-ask-user.js';\n\n/**\n * Register the `ask_user` tool + its handler, and hide its bubble segment\n * (it is a UI-only tool presented via the elicitation panel, not a tool pill).\n * Explicit setup — rather than a top-level import side-effect — keeps the\n * aparteGlobalConfig singleton mutation predictable in SSR/test and tree-shaking\n * friendly. Call once at application startup.\n */\nexport function setupAskUser(options: AskUserSetupOptions = {}, config: AparteConfig = aparteGlobalConfig): void {\n // The bounds the schema puts on the model are the HOST's, so they arrive here\n // rather than being frozen into a constant. Defaults are the normal call.\n const tool = createAskUserTool(options);\n config.registerTool(tool, askUserHandler);\n\n if (options.receipt === false) {\n config.registerToolRenderer(tool.name, { render: () => '' });\n return;\n }\n\n /*\n * The conversation keeps the record.\n *\n * This used to be `render: () => ''` — render nothing, \"it is a UI-only tool\" —\n * and the panel lives in the composer, so once it was answered the transcript\n * held no trace that the assistant had asked anything or that the user had\n * answered. Scroll back and the exchange is simply missing, which is not what a\n * conversation is for; every product that asks a structured question puts the\n * question and the chosen answer in the thread.\n *\n * The pieces were all here and wired to nothing: `questionReceiptRenderer` has\n * existed with its own markup, styles and eleven tests, exported and registered\n * by nobody, while the renderer that WOULD have shown something returned the\n * empty string. Another consequence of a surface no example ever ran.\n *\n * Registered too, so its `getStyles()` reaches the document and an app that\n * builds `question-receipt` segments of its own gets the same card.\n */\n registerSegmentRenderer(questionReceiptRenderer, config);\n config.registerToolRenderer(tool.name, {\n // The structured twin rides along: the receipt reads it first and only parses\n // the prose for a result that came from elsewhere. It was left out here, so\n // the structured path the receipt exists for was never taken on the real one.\n render: (segment) => buildReceipt({\n input: segment.toolCall.input, result: segment.result, structuredResult: segment.structuredResult,\n }),\n });\n}\n\nexport { createAskUserTool, askUserHandler, ASK_USER_DECLINED } from './ask-user.js';\nexport type { AskUserToolOptions, AskUserSetupOptions, AskUserAnswer, AskUserStructuredResult } from './ask-user.js';\nexport type { AskUserOption, AskUserItem, AskUserDetail } from './ask-user.js';\n\nexport { AparteAskUser } from './aparte-ask-user.js';\n\nexport { questionReceiptRenderer } from './question-receipt.renderer.js';\nexport { buildReceipt, receiptRows } from './receipt.js';\nexport type { ReceiptRow, ReceiptSource } from './receipt.js';\nexport type { QuestionReceiptSegment } from './question-receipt.renderer.js';\n\nexport type { AparteTool, AparteToolHandler, AparteToolCall, AparteToolResult } from '@aparte/core';\n"],"names":[],"mappings":";;;AAoBA,MAAM,MAAM;AAEL,MAAM,0BAAyE;AAAA,EAClF,MAAM;AAAA,EAEN,OAAO,KAAK;AAGR,QAAI,IAAI,UAAU;AACd,aAAO,oJAAoJ,IAAI,IAAI,EAAE,CAAC;AAAA,8EACpG,IAAI,IAAI,MAAM,CAAC;AAAA;AAAA,IAErF;AACA,WAAO,oHAAoH,IAAI,IAAI,EAAE,CAAC;AAAA,sEACxE,IAAI,IAAI,QAAQ,CAAC;AAAA;AAAA,oEAEnB,IAAI,IAAI,MAAM,CAAC;AAAA;AAAA,EAE/E;AAAA,EAEA,OAAO,IAAI,KAAK;AACZ,UAAM,IAAI,GAAG,cAAc,oCAAoC;AAC/D,QAAI,EAAG,GAAE,cAAc,IAAI;AAC3B,UAAM,IAAI,GAAG,cAAc,kCAAkC;AAC7D,QAAI,EAAG,GAAE,cAAc,IAAI;AAAA,EAC/B;AAAA,EAEA,YAAY;AACR,WAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA+DX;AACJ;ACrFO,MAAM,sBAAsB,kBAAkB;AAAC;AAEtD,IAAI,OAAO,mBAAmB,eAAe,CAAC,eAAe,IAAI,iBAAiB,GAAG;AACjF,iBAAe,OAAO,mBAAmB,aAAa;AAC1D;ACFO,SAAS,aAAa,UAA+B,IAAI,SAAuB,oBAA0B;AAG7G,QAAM,OAAO,kBAAkB,OAAO;AACtC,SAAO,aAAa,MAAM,cAAc;AAExC,MAAI,QAAQ,YAAY,OAAO;AAC3B,WAAO,qBAAqB,KAAK,MAAM,EAAE,QAAQ,MAAM,IAAI;AAC3D;AAAA,EACJ;AAoBA,0BAAwB,yBAAyB,MAAM;AACvD,SAAO,qBAAqB,KAAK,MAAM;AAAA;AAAA;AAAA;AAAA,IAInC,QAAQ,CAAC,YAAY,aAAa;AAAA,MAC9B,OAAO,QAAQ,SAAS;AAAA,MAAO,QAAQ,QAAQ;AAAA,MAAQ,kBAAkB,QAAQ;AAAA,IAAA,CACpF;AAAA,EAAA,CACJ;AACL;"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/question-receipt.renderer.ts","../src/aparte-ask-user.ts","../src/index.ts"],"sourcesContent":["import type { AparteSegmentRenderer } from '@aparte/core';\nimport { escapeAttr } from '@aparte/core';\n\nexport interface QuestionReceiptSegment {\n id: string;\n type: 'question-receipt';\n isStreaming?: boolean;\n question: string;\n answer: string;\n /**\n * The user declined the request: `answer` is then the outcome sentence, not an\n * answer to `question`, and the card shows it alone in the mark's quiet voice.\n * The tool renderer's DOM path had this branch; an app emitting the segment\n * itself had no way to say it.\n */\n declined?: boolean;\n}\n\n// Core owns the escaping; this alias keeps the call sites short. The inlined\n// copy that used to live here left the apostrophe through.\nconst esc = escapeAttr;\n\nexport const questionReceiptRenderer: AparteSegmentRenderer<QuestionReceiptSegment> = {\n type: 'question-receipt',\n\n render(seg) {\n // The card wears core's mark (display/mark.css) — see `buildReceipt`, the DOM\n // path, for why an answer is success and a decline is quiet.\n if (seg.declined) {\n return `<div class=\"aparte-segment aparte-tag aparte-question-receipt aparte-mark aparte-mark--quiet aparte-question-receipt--declined\" data-segment-id=\"${esc(seg.id)}\">\n <span class=\"aparte-tag__label aparte-question-receipt__answer--declined\">${esc(seg.answer)}</span>\n</div>`;\n }\n return `<div class=\"aparte-segment aparte-tag aparte-question-receipt aparte-mark aparte-mark--success\" data-segment-id=\"${esc(seg.id)}\">\n <span class=\"aparte-tag__label aparte-question-receipt__question\">${esc(seg.question)}</span>\n <span class=\"aparte-question-receipt__sep\">→</span>\n <span class=\"aparte-tag__label aparte-question-receipt__answer\">${esc(seg.answer)}</span>\n</div>`;\n },\n\n update(el, seg) {\n const q = el.querySelector('.aparte-question-receipt__question');\n if (q) q.textContent = seg.question;\n const a = el.querySelector('.aparte-question-receipt__answer');\n if (a) a.textContent = seg.answer;\n },\n\n getStyles() {\n return `\n/* ── Question Receipt Card ──────────────────────────────────────────────── */\n/* Several questions leave several cards, stacked. The tool renderer builds this\n group; the card below is shared with a question-receipt segment an app emits\n itself. (No backticks in here: this whole block is a template literal.)\n\n THE CARD IS AN .aparte-tag. It is a pill holding a truncating label, which is what\n that recipe is, and it used to redeclare the whole thing: inline-flex, gap, padding,\n border, radius, surface background, max-width — nine lines that core already owned.\n What is left below is only what a tag has no opinion about (the entrance, and the\n share of the width each half gets) plus this card's own measures, expressed as the\n tag's tokens so they land ON the recipe rather than beside it.\n\n This is also the only place in the repo where a PLUGIN reaches core's recipes, and\n that is the point: the recipes are plain classes on a stylesheet core already ships,\n so a plugin needs no import, no client, and no build step to use them. A capability\n that only core itself can reach would not be one. */\n/* A declined request: the outcome, in the muted voice of something that did not\n happen — not the green of an answer given. */\n.aparte-question-receipt__answer--declined {\n color: var(--aparte-text-muted);\n font-style: italic;\n}\n.aparte-question-receipt__group {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--aparte-space-2);\n}\n.aparte-question-receipt {\n --aparte-tag-gap: var(--aparte-space-4);\n --aparte-tag-padding: var(--aparte-space-3) var(--aparte-space-6);\n --aparte-tag-radius: var(--aparte-radius-full);\n --aparte-tag-font-size: var(--aparte-font-size-md);\n overflow: hidden;\n animation: aparte-question-receipt-appear var(--aparte-duration-slow) ease-out both;\n}\n/* The question yields the room, the answer keeps it: an answer cut in half is a\n receipt that records nothing, and the question is usually the re-readable half. */\n/* The neutral as text, not the muted token: muted on the success mark is 4.29:1. */\n.aparte-question-receipt__question {\n color: var(--aparte-neutral-ink, var(--aparte-text-muted));\n flex-shrink: 1;\n min-width: 0;\n}\n.aparte-question-receipt__sep {\n color: var(--aparte-text-muted);\n opacity: 0.4;\n flex-shrink: 0;\n}\n.aparte-question-receipt__answer {\n /* The strong text colour, not the success green: the answer is a fact of the\n transcript, not a verdict, and green was the one hue outside the palette on the\n whole page (UI audit 2026-08-28, §8.3 #4). The weight does the emphasis. */\n color: var(--aparte-text);\n font-weight: var(--aparte-font-weight-semibold);\n flex-shrink: 0;\n max-width: 55%;\n}\n@keyframes aparte-question-receipt-appear {\n from { opacity: 0; transform: translateY(var(--aparte-space-2)); }\n to { opacity: 1; transform: translateY(0); }\n}\n`;\n },\n};\n","/**\n * <aparte-ask-user> — a semantic alias of core's <aparte-elicitation>.\n *\n * ask_user runs on the core elicitation primitive; it has no bespoke Web\n * Component. This thin subclass lets you mount the presenter with intent-revealing\n * markup (`<aparte-ask-user>`) instead of the generic `<aparte-elicitation>`.\n * The two are interchangeable.\n */\n\nimport { AparteElicitation } from '@aparte/core';\n\n/**\n * A semantic alias of core's `<aparte-elicitation>`: same presenter, intent-revealing\n * tag. `ask_user` runs on the core elicitation primitive and has no bespoke component,\n * so this subclass adds no behaviour — it exists so markup can say what it means. The\n * two are interchangeable, and neither dispatches anything.\n *\n * @element aparte-ask-user\n *\n * @example\n * <!-- Identical to <aparte-elicitation>; mount either one, never both. -->\n * <aparte-chat>\n * <aparte-chat-viewport></aparte-chat-viewport>\n * <aparte-ask-user></aparte-ask-user>\n * <aparte-composer></aparte-composer>\n * </aparte-chat>\n */\nexport class AparteAskUser extends AparteElicitation {}\n\nif (typeof customElements !== 'undefined' && !customElements.get('aparte-ask-user')) {\n customElements.define('aparte-ask-user', AparteAskUser);\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'aparte-ask-user': AparteAskUser;\n }\n}\n","/**\n * @aparte/plugin-ask-user\n *\n * The built-in `ask_user` tool — a thin adapter over the core elicitation\n * primitive. The AI asks the user a structured choice; the handler forwards it to\n * `requestUserInput`, presented by `<aparte-elicitation>` (or the semantic\n * `<aparte-ask-user>` alias registered by importing this package).\n *\n * Usage:\n * import { setupAskUser } from '@aparte/plugin-ask-user';\n * setupAskUser(); // registers the tool + hides its bubble segment\n * // mount <aparte-elicitation> (or <aparte-ask-user>) in your chat\n */\n\nimport { aparteGlobalConfig, registerSegmentRenderer, type AparteConfig } from '@aparte/core';\nimport { createAskUserTool, askUserHandler, type AskUserSetupOptions } from './ask-user.js';\nimport { questionReceiptRenderer } from './question-receipt.renderer.js';\nimport { buildReceipt } from './receipt.js';\n\n// Register the <aparte-ask-user> semantic alias (subclass of <aparte-elicitation>).\nimport './aparte-ask-user.js';\n\n/**\n * Register the `ask_user` tool + its handler, and hide its bubble segment\n * (it is a UI-only tool presented via the elicitation panel, not a tool pill).\n * Explicit setup — rather than a top-level import side-effect — keeps the\n * aparteGlobalConfig singleton mutation predictable in SSR/test and tree-shaking\n * friendly. Call once at application startup.\n */\nexport function setupAskUser(options: AskUserSetupOptions = {}, config: AparteConfig = aparteGlobalConfig): void {\n // The bounds the schema puts on the model are the HOST's, so they arrive here\n // rather than being frozen into a constant. Defaults are the normal call.\n const tool = createAskUserTool(options);\n config.registerTool(tool, askUserHandler);\n\n if (options.receipt === false) {\n config.registerToolRenderer(tool.name, { render: () => '' });\n return;\n }\n\n /*\n * The conversation keeps the record.\n *\n * This used to be `render: () => ''` — render nothing, \"it is a UI-only tool\" —\n * and the panel lives in the composer, so once it was answered the transcript\n * held no trace that the assistant had asked anything or that the user had\n * answered. Scroll back and the exchange is simply missing, which is not what a\n * conversation is for; every product that asks a structured question puts the\n * question and the chosen answer in the thread.\n *\n * The pieces were all here and wired to nothing: `questionReceiptRenderer` has\n * existed with its own markup, styles and eleven tests, exported and registered\n * by nobody, while the renderer that WOULD have shown something returned the\n * empty string. Another consequence of a surface no example ever ran.\n *\n * Registered too, so its `getStyles()` reaches the document and an app that\n * builds `question-receipt` segments of its own gets the same card.\n */\n registerSegmentRenderer(questionReceiptRenderer, config);\n config.registerToolRenderer(tool.name, {\n // The structured twin rides along: the receipt reads it first and only parses\n // the prose for a result that came from elsewhere. It was left out here, so\n // the structured path the receipt exists for was never taken on the real one.\n render: (segment) => buildReceipt({\n input: segment.toolCall.input, result: segment.result, structuredResult: segment.structuredResult,\n }),\n });\n}\n\nexport { createAskUserTool, askUserHandler, ASK_USER_DECLINED } from './ask-user.js';\nexport type { AskUserToolOptions, AskUserSetupOptions, AskUserAnswer, AskUserStructuredResult } from './ask-user.js';\nexport type { AskUserOption, AskUserItem, AskUserDetail } from './ask-user.js';\n\nexport { AparteAskUser } from './aparte-ask-user.js';\n\nexport { questionReceiptRenderer } from './question-receipt.renderer.js';\nexport { buildReceipt, receiptRows } from './receipt.js';\nexport type { ReceiptRow, ReceiptSource } from './receipt.js';\nexport type { QuestionReceiptSegment } from './question-receipt.renderer.js';\n\nexport type { AparteTool, AparteToolHandler, AparteToolCall, AparteToolResult } from '@aparte/core';\n"],"names":[],"mappings":";;;AAoBA,MAAM,MAAM;AAEL,MAAM,0BAAyE;AAAA,EAClF,MAAM;AAAA,EAEN,OAAO,KAAK;AAGR,QAAI,IAAI,UAAU;AACd,aAAO,oJAAoJ,IAAI,IAAI,EAAE,CAAC;AAAA,8EACpG,IAAI,IAAI,MAAM,CAAC;AAAA;AAAA,IAErF;AACA,WAAO,oHAAoH,IAAI,IAAI,EAAE,CAAC;AAAA,sEACxE,IAAI,IAAI,QAAQ,CAAC;AAAA;AAAA,oEAEnB,IAAI,IAAI,MAAM,CAAC;AAAA;AAAA,EAE/E;AAAA,EAEA,OAAO,IAAI,KAAK;AACZ,UAAM,IAAI,GAAG,cAAc,oCAAoC;AAC/D,QAAI,EAAG,GAAE,cAAc,IAAI;AAC3B,UAAM,IAAI,GAAG,cAAc,kCAAkC;AAC7D,QAAI,EAAG,GAAE,cAAc,IAAI;AAAA,EAC/B;AAAA,EAEA,YAAY;AACR,WAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgEX;AACJ;ACtFO,MAAM,sBAAsB,kBAAkB;AAAC;AAEtD,IAAI,OAAO,mBAAmB,eAAe,CAAC,eAAe,IAAI,iBAAiB,GAAG;AACjF,iBAAe,OAAO,mBAAmB,aAAa;AAC1D;ACFO,SAAS,aAAa,UAA+B,IAAI,SAAuB,oBAA0B;AAG7G,QAAM,OAAO,kBAAkB,OAAO;AACtC,SAAO,aAAa,MAAM,cAAc;AAExC,MAAI,QAAQ,YAAY,OAAO;AAC3B,WAAO,qBAAqB,KAAK,MAAM,EAAE,QAAQ,MAAM,IAAI;AAC3D;AAAA,EACJ;AAoBA,0BAAwB,yBAAyB,MAAM;AACvD,SAAO,qBAAqB,KAAK,MAAM;AAAA;AAAA;AAAA;AAAA,IAInC,QAAQ,CAAC,YAAY,aAAa;AAAA,MAC9B,OAAO,QAAQ,SAAS;AAAA,MAAO,QAAQ,QAAQ;AAAA,MAAQ,kBAAkB,QAAQ;AAAA,IAAA,CACpF;AAAA,EAAA,CACJ;AACL;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"question-receipt.renderer.d.ts","sourceRoot":"","sources":["../src/question-receipt.renderer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAG1D,MAAM,WAAW,sBAAsB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,kBAAkB,CAAC;IACzB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACtB;AAMD,eAAO,MAAM,uBAAuB,EAAE,qBAAqB,CAAC,sBAAsB,
|
|
1
|
+
{"version":3,"file":"question-receipt.renderer.d.ts","sourceRoot":"","sources":["../src/question-receipt.renderer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAG1D,MAAM,WAAW,sBAAsB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,kBAAkB,CAAC;IACzB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACtB;AAMD,eAAO,MAAM,uBAAuB,EAAE,qBAAqB,CAAC,sBAAsB,CA2FjF,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aparte/plugin-ask-user",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"homepage": "https://apartejs.dev/plugins/ask-user/",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -19,6 +19,26 @@
|
|
|
19
19
|
"types": "./dist/index.node.d.ts",
|
|
20
20
|
"default": "./dist/index.node.js"
|
|
21
21
|
},
|
|
22
|
+
"worker": {
|
|
23
|
+
"types": "./dist/index.node.d.ts",
|
|
24
|
+
"default": "./dist/index.node.js"
|
|
25
|
+
},
|
|
26
|
+
"workerd": {
|
|
27
|
+
"types": "./dist/index.node.d.ts",
|
|
28
|
+
"default": "./dist/index.node.js"
|
|
29
|
+
},
|
|
30
|
+
"edge-light": {
|
|
31
|
+
"types": "./dist/index.node.d.ts",
|
|
32
|
+
"default": "./dist/index.node.js"
|
|
33
|
+
},
|
|
34
|
+
"deno": {
|
|
35
|
+
"types": "./dist/index.node.d.ts",
|
|
36
|
+
"default": "./dist/index.node.js"
|
|
37
|
+
},
|
|
38
|
+
"bun": {
|
|
39
|
+
"types": "./dist/index.node.d.ts",
|
|
40
|
+
"default": "./dist/index.node.js"
|
|
41
|
+
},
|
|
22
42
|
"types": "./dist/index.d.ts",
|
|
23
43
|
"import": "./dist/index.js",
|
|
24
44
|
"default": "./dist/index.js"
|
|
@@ -33,13 +53,13 @@
|
|
|
33
53
|
"node": ">=18"
|
|
34
54
|
},
|
|
35
55
|
"peerDependencies": {
|
|
36
|
-
"@aparte/core": ">=0.
|
|
56
|
+
"@aparte/core": ">=0.20.0 <1.0.0"
|
|
37
57
|
},
|
|
38
58
|
"devDependencies": {
|
|
39
59
|
"@custom-elements-manifest/analyzer": "^0.11.0",
|
|
40
60
|
"typescript": "^5.4.0",
|
|
41
61
|
"vite": "^6.0.0",
|
|
42
|
-
"@aparte/core": "0.
|
|
62
|
+
"@aparte/core": "0.20.0"
|
|
43
63
|
},
|
|
44
64
|
"keywords": [
|
|
45
65
|
"aparte",
|
|
@@ -60,7 +80,7 @@
|
|
|
60
80
|
},
|
|
61
81
|
"scripts": {
|
|
62
82
|
"dev": "vite",
|
|
63
|
-
"build": "vite build && tsc -b --emitDeclarationOnly --force && pnpm run analyze",
|
|
83
|
+
"build": "vite build && tsc -b --emitDeclarationOnly --force && pnpm run analyze && node ../../../scripts/stamp-dist.mjs",
|
|
64
84
|
"analyze": "cem analyze",
|
|
65
85
|
"preview": "vite preview",
|
|
66
86
|
"test": "vitest",
|