@assistant-ui/react-generative-ui 0.0.12 → 0.0.13
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.
- package/dist/renderGenerativeUI.js +2 -1
- package/dist/renderGenerativeUI.js.map +1 -1
- package/dist/slack/toSlackBlocks.js +3 -2
- package/dist/slack/toSlackBlocks.js.map +1 -1
- package/dist/teams/toAdaptiveCard.js +4 -2
- package/dist/teams/toAdaptiveCard.js.map +1 -1
- package/dist/teams/toTeamsAttachments.js +3 -2
- package/dist/teams/toTeamsAttachments.js.map +1 -1
- package/dist/vocabulary/data.js +3 -2
- package/dist/vocabulary/data.js.map +1 -1
- package/package.json +5 -5
|
@@ -17,7 +17,8 @@ const isElement = (node) => typeof node === "object" && node !== null && !Array.
|
|
|
17
17
|
*/
|
|
18
18
|
function renderGenerativeUI(node, library, context = DEFAULT_CONTEXT) {
|
|
19
19
|
const meta = getPartialJsonObjectMeta(node);
|
|
20
|
-
|
|
20
|
+
const partialPath = meta?.state === "partial" ? meta.partialPath : void 0;
|
|
21
|
+
return renderNode(normalizeUINode(node, partialPath), library, context);
|
|
21
22
|
}
|
|
22
23
|
function renderNode(node, library, context) {
|
|
23
24
|
if (node == null || typeof node === "boolean") return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderGenerativeUI.js","names":[],"sources":["../src/renderGenerativeUI.tsx"],"sourcesContent":["import { getPartialJsonObjectMeta } from \"assistant-stream/utils\";\nimport { Fragment, type ReactNode } from \"react\";\nimport {\n normalizeUINode,\n type NormalizedUIElement,\n type NormalizedUINode,\n} from \"./ir\";\nimport type { GenerativeUILibrary, GenerativeUIRenderContext } from \"./types\";\n\nconst DEFAULT_CONTEXT: GenerativeUIRenderContext = { status: \"done\" };\n\nconst isElement = (node: NormalizedUINode): node is NormalizedUIElement =>\n typeof node === \"object\" && node !== null && !Array.isArray(node);\n\n/**\n * Renders a generative-ui tree against a {@link GenerativeUILibrary}.\n *\n * The model emits each node as a flat object `{ $type, ...props }`. We first\n * normalize that wire form into the canonical {@link NormalizedUINode} (with\n * `children` lifted to a reserved top-level key), then render: each `type` is\n * looked up in the library and its `props` are passed to the component's\n * `render(props, context)`, with `children` rendered recursively so components\n * can nest.\n */\nexport function renderGenerativeUI(\n node: unknown,\n library: GenerativeUILibrary,\n context: GenerativeUIRenderContext = DEFAULT_CONTEXT,\n): ReactNode {\n // Tool args are parsed incrementally, and the parse meta records which path\n // is still mid-arrival, so normalization can hold back a node whose `$type`\n // string has not finished streaming.\n const meta = getPartialJsonObjectMeta(node as Record<symbol, unknown>);\n const partialPath = meta?.state === \"partial\" ? meta.partialPath : undefined;\n return renderNode(normalizeUINode(node, partialPath), library, context);\n}\n\nfunction renderNode(\n node: NormalizedUINode,\n library: GenerativeUILibrary,\n context: GenerativeUIRenderContext,\n): ReactNode {\n if (node == null || typeof node === \"boolean\") return null;\n if (typeof node === \"string\" || typeof node === \"number\") return node;\n if (Array.isArray(node)) {\n // Use a model-provided stable key when present. Otherwise, keep the\n // positional fallback: pairing the index with the node's kind means that\n // when the kind at an index changes, React remounts instead of handing a\n // streaming node's hook state to a different component.\n return node.map((child, index) => (\n <Fragment key={nodeKey(child, index)}>\n {renderNode(child, library, context)}\n </Fragment>\n ));\n }\n return isElement(node) ? renderElement(node, library, context) : null;\n}\n\nfunction renderElement(\n element: NormalizedUIElement,\n library: GenerativeUILibrary,\n context: GenerativeUIRenderContext,\n): ReactNode {\n const entry = library[element.type];\n if (!entry) {\n reportUnknownComponent(element.type, Object.keys(library));\n return null;\n }\n\n // Components that opt out of prop streaming wait until their props are\n // complete rather than rendering from a partial parse.\n if (!entry.streamProperties && context.status === \"streaming\") return null;\n\n // `children` is a reserved top-level key on the normalized element, not a\n // prop, so it is not in `props`. Inject the framework props last so the model\n // can never override them. `$action` is likewise reserved and stripped from\n // the prop bag during normalization, so it is re-injected here for components\n // that carry behavior (e.g. `Button`).\n const props: Record<string, unknown> = {\n ...element.props,\n $status: context.status,\n };\n if (context.dispatch !== undefined) {\n props[\"$dispatch\"] = context.dispatch;\n }\n if (element.action !== undefined) {\n props[\"$action\"] = element.action;\n }\n if (element.children !== undefined) {\n props[\"children\"] = renderNode(element.children, library, context);\n }\n\n return <GenerativeUIComponentRenderer render={entry.render} props={props} />;\n}\n\n/**\n * Mounts a single node's `render` on its own fiber so the function may use\n * hooks and hold state independently of its siblings and parent.\n */\nfunction GenerativeUIComponentRenderer({\n render,\n props,\n}: {\n render: (props: any) => ReactNode;\n props: Record<string, unknown>;\n}): ReactNode {\n return render(props);\n}\n\n/** A coarse kind tag for a child, used in its list key so a node changing kind\n * at a given index forces a remount rather than a wrong-fiber reuse. */\nfunction nodeKind(node: NormalizedUINode): string {\n if (node == null || typeof node === \"boolean\") return \"\";\n if (typeof node === \"string\" || typeof node === \"number\") return \"#text\";\n if (Array.isArray(node)) return \"#array\";\n return isElement(node) ? node.type : \"\";\n}\n\nfunction nodeKey(node: NormalizedUINode, index: number): string {\n return isElement(node) &&\n (typeof node.key === \"string\" || typeof node.key === \"number\")\n ? `model:${node.key}`\n : `${index}:${nodeKind(node)}`;\n}\n\nfunction reportUnknownComponent(type: string, available: string[]): void {\n if (process.env[\"NODE_ENV\"] !== \"production\") {\n // eslint-disable-next-line no-console\n console.error(\n `[@assistant-ui/react-generative-ui] Unknown component \"${type}\". ` +\n `Available components: ${available.join(\", \") || \"(none)\"}.`,\n );\n }\n}\n"],"mappings":";;;;;AASA,MAAM,kBAA6C,EAAE,QAAQ,OAAO;AAEpE,MAAM,aAAa,SACjB,OAAO,SAAS,YAAY,SAAS,QAAQ,CAAC,MAAM,QAAQ,IAAI;;;;;;;;;;;AAYlE,SAAgB,mBACd,MACA,SACA,UAAqC,iBAC1B;CAIX,MAAM,OAAO,yBAAyB,IAA+B;
|
|
1
|
+
{"version":3,"file":"renderGenerativeUI.js","names":[],"sources":["../src/renderGenerativeUI.tsx"],"sourcesContent":["import { getPartialJsonObjectMeta } from \"assistant-stream/utils\";\nimport { Fragment, type ReactNode } from \"react\";\nimport {\n normalizeUINode,\n type NormalizedUIElement,\n type NormalizedUINode,\n} from \"./ir\";\nimport type { GenerativeUILibrary, GenerativeUIRenderContext } from \"./types\";\n\nconst DEFAULT_CONTEXT: GenerativeUIRenderContext = { status: \"done\" };\n\nconst isElement = (node: NormalizedUINode): node is NormalizedUIElement =>\n typeof node === \"object\" && node !== null && !Array.isArray(node);\n\n/**\n * Renders a generative-ui tree against a {@link GenerativeUILibrary}.\n *\n * The model emits each node as a flat object `{ $type, ...props }`. We first\n * normalize that wire form into the canonical {@link NormalizedUINode} (with\n * `children` lifted to a reserved top-level key), then render: each `type` is\n * looked up in the library and its `props` are passed to the component's\n * `render(props, context)`, with `children` rendered recursively so components\n * can nest.\n */\nexport function renderGenerativeUI(\n node: unknown,\n library: GenerativeUILibrary,\n context: GenerativeUIRenderContext = DEFAULT_CONTEXT,\n): ReactNode {\n // Tool args are parsed incrementally, and the parse meta records which path\n // is still mid-arrival, so normalization can hold back a node whose `$type`\n // string has not finished streaming.\n const meta = getPartialJsonObjectMeta(node as Record<symbol, unknown>);\n const partialPath = meta?.state === \"partial\" ? meta.partialPath : undefined;\n return renderNode(normalizeUINode(node, partialPath), library, context);\n}\n\nfunction renderNode(\n node: NormalizedUINode,\n library: GenerativeUILibrary,\n context: GenerativeUIRenderContext,\n): ReactNode {\n if (node == null || typeof node === \"boolean\") return null;\n if (typeof node === \"string\" || typeof node === \"number\") return node;\n if (Array.isArray(node)) {\n // Use a model-provided stable key when present. Otherwise, keep the\n // positional fallback: pairing the index with the node's kind means that\n // when the kind at an index changes, React remounts instead of handing a\n // streaming node's hook state to a different component.\n return node.map((child, index) => (\n <Fragment key={nodeKey(child, index)}>\n {renderNode(child, library, context)}\n </Fragment>\n ));\n }\n return isElement(node) ? renderElement(node, library, context) : null;\n}\n\nfunction renderElement(\n element: NormalizedUIElement,\n library: GenerativeUILibrary,\n context: GenerativeUIRenderContext,\n): ReactNode {\n const entry = library[element.type];\n if (!entry) {\n reportUnknownComponent(element.type, Object.keys(library));\n return null;\n }\n\n // Components that opt out of prop streaming wait until their props are\n // complete rather than rendering from a partial parse.\n if (!entry.streamProperties && context.status === \"streaming\") return null;\n\n // `children` is a reserved top-level key on the normalized element, not a\n // prop, so it is not in `props`. Inject the framework props last so the model\n // can never override them. `$action` is likewise reserved and stripped from\n // the prop bag during normalization, so it is re-injected here for components\n // that carry behavior (e.g. `Button`).\n const props: Record<string, unknown> = {\n ...element.props,\n $status: context.status,\n };\n if (context.dispatch !== undefined) {\n props[\"$dispatch\"] = context.dispatch;\n }\n if (element.action !== undefined) {\n props[\"$action\"] = element.action;\n }\n if (element.children !== undefined) {\n props[\"children\"] = renderNode(element.children, library, context);\n }\n\n return <GenerativeUIComponentRenderer render={entry.render} props={props} />;\n}\n\n/**\n * Mounts a single node's `render` on its own fiber so the function may use\n * hooks and hold state independently of its siblings and parent.\n */\nfunction GenerativeUIComponentRenderer({\n render,\n props,\n}: {\n render: (props: any) => ReactNode;\n props: Record<string, unknown>;\n}): ReactNode {\n return render(props);\n}\n\n/** A coarse kind tag for a child, used in its list key so a node changing kind\n * at a given index forces a remount rather than a wrong-fiber reuse. */\nfunction nodeKind(node: NormalizedUINode): string {\n if (node == null || typeof node === \"boolean\") return \"\";\n if (typeof node === \"string\" || typeof node === \"number\") return \"#text\";\n if (Array.isArray(node)) return \"#array\";\n return isElement(node) ? node.type : \"\";\n}\n\nfunction nodeKey(node: NormalizedUINode, index: number): string {\n return isElement(node) &&\n (typeof node.key === \"string\" || typeof node.key === \"number\")\n ? `model:${node.key}`\n : `${index}:${nodeKind(node)}`;\n}\n\nfunction reportUnknownComponent(type: string, available: string[]): void {\n if (process.env[\"NODE_ENV\"] !== \"production\") {\n // eslint-disable-next-line no-console\n console.error(\n `[@assistant-ui/react-generative-ui] Unknown component \"${type}\". ` +\n `Available components: ${available.join(\", \") || \"(none)\"}.`,\n );\n }\n}\n"],"mappings":";;;;;AASA,MAAM,kBAA6C,EAAE,QAAQ,OAAO;AAEpE,MAAM,aAAa,SACjB,OAAO,SAAS,YAAY,SAAS,QAAQ,CAAC,MAAM,QAAQ,IAAI;;;;;;;;;;;AAYlE,SAAgB,mBACd,MACA,SACA,UAAqC,iBAC1B;CAIX,MAAM,OAAO,yBAAyB,IAA+B;CACrE,MAAM,cAAc,MAAM,UAAU,YAAY,KAAK,cAAc,KAAA;CACnE,OAAO,WAAW,gBAAgB,MAAM,WAAW,GAAG,SAAS,OAAO;AACxE;AAEA,SAAS,WACP,MACA,SACA,SACW;CACX,IAAI,QAAQ,QAAQ,OAAO,SAAS,WAAW,OAAO;CACtD,IAAI,OAAO,SAAS,YAAY,OAAO,SAAS,UAAU,OAAO;CACjE,IAAI,MAAM,QAAQ,IAAI,GAKpB,OAAO,KAAK,KAAK,OAAO,UACtB,oBAAC,UAAD,EAAA,UACG,WAAW,OAAO,SAAS,OAAO,EAC3B,GAFK,QAAQ,OAAO,KAAK,CAEzB,CACX;CAEH,OAAO,UAAU,IAAI,IAAI,cAAc,MAAM,SAAS,OAAO,IAAI;AACnE;AAEA,SAAS,cACP,SACA,SACA,SACW;CACX,MAAM,QAAQ,QAAQ,QAAQ;CAC9B,IAAI,CAAC,OAAO;EACV,uBAAuB,QAAQ,MAAM,OAAO,KAAK,OAAO,CAAC;EACzD,OAAO;CACT;CAIA,IAAI,CAAC,MAAM,oBAAoB,QAAQ,WAAW,aAAa,OAAO;CAOtE,MAAM,QAAiC;EACrC,GAAG,QAAQ;EACX,SAAS,QAAQ;CACnB;CACA,IAAI,QAAQ,aAAa,KAAA,GACvB,MAAM,eAAe,QAAQ;CAE/B,IAAI,QAAQ,WAAW,KAAA,GACrB,MAAM,aAAa,QAAQ;CAE7B,IAAI,QAAQ,aAAa,KAAA,GACvB,MAAM,cAAc,WAAW,QAAQ,UAAU,SAAS,OAAO;CAGnE,OAAO,oBAAC,+BAAD;EAA+B,QAAQ,MAAM;EAAe;CAAQ,CAAA;AAC7E;;;;;AAMA,SAAS,8BAA8B,EACrC,QACA,SAIY;CACZ,OAAO,OAAO,KAAK;AACrB;;;AAIA,SAAS,SAAS,MAAgC;CAChD,IAAI,QAAQ,QAAQ,OAAO,SAAS,WAAW,OAAO;CACtD,IAAI,OAAO,SAAS,YAAY,OAAO,SAAS,UAAU,OAAO;CACjE,IAAI,MAAM,QAAQ,IAAI,GAAG,OAAO;CAChC,OAAO,UAAU,IAAI,IAAI,KAAK,OAAO;AACvC;AAEA,SAAS,QAAQ,MAAwB,OAAuB;CAC9D,OAAO,UAAU,IAAI,MAClB,OAAO,KAAK,QAAQ,YAAY,OAAO,KAAK,QAAQ,YACnD,SAAS,KAAK,QACd,GAAG,MAAM,GAAG,SAAS,IAAI;AAC/B;AAEA,SAAS,uBAAuB,MAAc,WAA2B;CACvE,IAAI,QAAQ,IAAI,gBAAgB,cAE9B,QAAQ,MACN,0DAA0D,KAAK,2BACpC,UAAU,KAAK,IAAI,KAAK,SAAS,EAC9D;AAEJ"}
|
|
@@ -616,10 +616,11 @@ function toSlackBlocks(node, options) {
|
|
|
616
616
|
dataTableCharacters: 0
|
|
617
617
|
};
|
|
618
618
|
try {
|
|
619
|
-
const
|
|
619
|
+
const bounded = boundSpec(node, (reason) => {
|
|
620
620
|
const detail = reason === "budget" ? `the tree was truncated after ${NODE_BUDGET} nodes.` : reason === "cycle" ? "a self-referencing node was dropped." : reason === "depth" ? `nodes deeper than 64 levels were dropped.` : `children were clamped to 200 entries.`;
|
|
621
621
|
warn(context, "clamped", "Root", detail);
|
|
622
|
-
})
|
|
622
|
+
});
|
|
623
|
+
const { root } = normalizeSpec(bounded);
|
|
623
624
|
const converted = convertSequence(root, context, 0);
|
|
624
625
|
if (converted.length <= context.blockCap) return {
|
|
625
626
|
blocks: converted,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toSlackBlocks.js","names":[],"sources":["../../src/slack/toSlackBlocks.ts"],"sourcesContent":["import {\n normalizeSpec,\n type NormalizedUIElement,\n type NormalizedUINode,\n} from \"../ir\";\nimport {\n ACTION_ID_CAP,\n ACTIONS_ELEMENT_CAP,\n ALERT_TEXT_CAP,\n BUTTON_VALUE_CAP,\n CARD_ACTIONS_CAP,\n CARD_BODY_CAP,\n CARD_SUBTEXT_CAP,\n CARD_TITLE_CAP,\n CAROUSEL_CARD_CAP,\n CAROUSEL_CARD_MIN,\n CHILDREN_CAP,\n CONTEXT_ELEMENT_CAP,\n CONTEXT_TEXT_CAP,\n DATA_TABLE_CHAR_BUDGET,\n DATA_TABLE_COLUMN_CAP,\n DATA_TABLE_ROW_CAP,\n FACT_FIELD_CAP,\n FACT_FIELD_TEXT_CAP,\n HEADER_TEXT_CAP,\n INPUT_LABEL_CAP,\n INTERACTIVE_TEXT_CAP,\n MARKDOWN_TEXT_BUDGET,\n MAX_TRAVERSAL_DEPTH,\n MESSAGE_BLOCK_CAP,\n MODAL_BLOCK_CAP,\n NODE_BUDGET,\n PLACEHOLDER_TEXT_CAP,\n RADIO_OPTION_CAP,\n SECTION_TEXT_CAP,\n SELECT_OPTION_CAP,\n buildAlertBlock,\n buildAlertFallback,\n buildBlockBudgetNote,\n buildCardBlock,\n buildCarouselBlock,\n buildDataTableBlock,\n} from \"./constants\";\nimport type {\n SlackActionElement,\n SlackBlock,\n SlackButtonElement,\n SlackCardBlock,\n SlackConversionWarning,\n SlackDataTableCell,\n SlackImageBlock,\n SlackOption,\n SlackPlainText,\n SlackTextObject,\n SlackBlocksResult,\n ToSlackBlocksOptions,\n} from \"./types\";\n\ntype ConversionContext = {\n readonly blockCap: number;\n readonly surface: \"message\" | \"modal\";\n readonly warnings: SlackConversionWarning[];\n markdownCharacters: number;\n markdownExhausted: boolean;\n dataTableCharacters: number;\n};\n\nconst INTERACTIVE_TYPES = new Set([\n \"Button\",\n \"Select\",\n \"DatePicker\",\n \"Checkbox\",\n \"RadioGroup\",\n]);\n\nconst DATE_PATTERN = /^\\d{4}-\\d{2}-\\d{2}$/;\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst isElement = (node: NormalizedUINode): node is NormalizedUIElement =>\n isRecord(node);\n\nconst asString = (value: unknown): string =>\n typeof value === \"string\" ? value : \"\";\n\nconst warn = (\n context: ConversionContext,\n code: SlackConversionWarning[\"code\"],\n component: string,\n detail: string,\n) => {\n context.warnings.push({ code, component, detail });\n};\n\nconst clampText = (\n value: string,\n cap: number,\n component: string,\n field: string,\n context: ConversionContext,\n): string => {\n if (value.length <= cap) return value;\n warn(\n context,\n \"clamped\",\n component,\n `${field} was clamped to ${cap} characters.`,\n );\n return value.slice(0, cap);\n};\n\nconst asActionId = (\n action: unknown,\n component: string,\n context: ConversionContext,\n): string =>\n clampText(\n isRecord(action) && typeof action[\"type\"] === \"string\"\n ? action[\"type\"]\n : \"action\",\n ACTION_ID_CAP,\n component,\n \"action_id\",\n context,\n );\n\nconst plainText = (text: string): SlackPlainText => ({\n type: \"plain_text\",\n text,\n});\n\nconst actionValue = (action: unknown): string | undefined => {\n if (!isRecord(action)) return undefined;\n const { type: _type, ...payload } = action;\n if (Object.keys(payload).length === 0) return undefined;\n try {\n const serialized = JSON.stringify(payload);\n return serialized === \"{}\" ? undefined : serialized;\n } catch {\n return undefined;\n }\n};\n\nconst buttonElement = (\n label: string,\n buttonStyle: unknown,\n action: unknown,\n component: string,\n context: ConversionContext,\n): SlackButtonElement => {\n const serializedValue = actionValue(action);\n let value = serializedValue;\n if (value !== undefined && value.length > BUTTON_VALUE_CAP) {\n warn(\n context,\n \"dropped\",\n component,\n \"value was dropped because the action payload was too large to round-trip.\",\n );\n value = undefined;\n }\n const style =\n buttonStyle === \"primary\" || buttonStyle === \"danger\"\n ? buttonStyle\n : undefined;\n return {\n type: \"button\",\n text: plainText(\n clampText(label, INTERACTIVE_TEXT_CAP, component, \"label\", context),\n ),\n action_id: asActionId(action, component, context),\n ...(value !== undefined ? { value } : {}),\n ...(style !== undefined ? { style } : {}),\n };\n};\n\nconst optionFrom = (\n value: unknown,\n component: string,\n context: ConversionContext,\n): SlackOption | undefined => {\n if (\n !isRecord(value) ||\n typeof value[\"label\"] !== \"string\" ||\n typeof value[\"value\"] !== \"string\"\n ) {\n return undefined;\n }\n return {\n text: plainText(\n clampText(\n value[\"label\"],\n INTERACTIVE_TEXT_CAP,\n component,\n \"option label\",\n context,\n ),\n ),\n value: value[\"value\"],\n };\n};\n\nconst toActionElement = (\n element: NormalizedUIElement,\n context: ConversionContext,\n): SlackActionElement | undefined => {\n const { props, action } = element;\n switch (element.type) {\n case \"Button\":\n return buttonElement(\n asString(props[\"label\"]),\n props[\"buttonStyle\"],\n action,\n \"Button\",\n context,\n );\n case \"Select\": {\n const rawOptions = Array.isArray(props[\"options\"])\n ? props[\"options\"]\n : [];\n if (rawOptions.length > SELECT_OPTION_CAP) {\n warn(\n context,\n \"clamped\",\n \"Select\",\n `options were clamped to ${SELECT_OPTION_CAP} entries.`,\n );\n }\n const options = rawOptions\n .slice(0, SELECT_OPTION_CAP)\n .map((option) => optionFrom(option, \"Select\", context))\n .filter((option): option is SlackOption => option !== undefined);\n const placeholder = clampText(\n asString(props[\"placeholder\"]),\n PLACEHOLDER_TEXT_CAP,\n \"Select\",\n \"placeholder\",\n context,\n );\n return {\n type: \"static_select\",\n action_id: asActionId(action, \"Select\", context),\n options,\n ...(placeholder ? { placeholder: plainText(placeholder) } : {}),\n };\n }\n case \"DatePicker\": {\n const rawDate = asString(props[\"value\"]);\n const initialDate = DATE_PATTERN.test(rawDate) ? rawDate : undefined;\n if (rawDate && initialDate === undefined) {\n warn(\n context,\n \"dropped\",\n \"DatePicker\",\n \"value did not match the YYYY-MM-DD format and was dropped.\",\n );\n }\n return {\n type: \"datepicker\",\n action_id: asActionId(action, \"DatePicker\", context),\n ...(initialDate !== undefined ? { initial_date: initialDate } : {}),\n };\n }\n case \"Checkbox\": {\n const label = clampText(\n asString(props[\"label\"]),\n INTERACTIVE_TEXT_CAP,\n \"Checkbox\",\n \"label\",\n context,\n );\n const name = asString(props[\"name\"]);\n const option: SlackOption = {\n text: plainText(label),\n value: name || label,\n };\n return {\n type: \"checkboxes\",\n action_id: asActionId(action, \"Checkbox\", context),\n options: [option],\n ...(props[\"defaultChecked\"] === true\n ? { initial_options: [option] }\n : {}),\n };\n }\n case \"RadioGroup\": {\n const rawOptions = Array.isArray(props[\"options\"])\n ? props[\"options\"]\n : [];\n if (rawOptions.length > RADIO_OPTION_CAP) {\n warn(\n context,\n \"clamped\",\n \"RadioGroup\",\n `options were clamped to ${RADIO_OPTION_CAP} entries.`,\n );\n }\n const options = rawOptions\n .slice(0, RADIO_OPTION_CAP)\n .map((option) => optionFrom(option, \"RadioGroup\", context))\n .filter((option): option is SlackOption => option !== undefined);\n const selectedValue =\n typeof props[\"value\"] === \"string\"\n ? props[\"value\"]\n : asString(props[\"defaultValue\"]);\n const initialOption = options.find(\n (option) => option.value === selectedValue,\n );\n return {\n type: \"radio_buttons\",\n action_id: asActionId(action, \"RadioGroup\", context),\n options,\n ...(initialOption !== undefined\n ? { initial_option: initialOption }\n : {}),\n };\n }\n default:\n return undefined;\n }\n};\n\nconst normalizedList = (\n node: NormalizedUINode | undefined,\n): NormalizedUINode[] => {\n if (node === undefined || node === null) return [];\n if (!Array.isArray(node)) return [node];\n return node.flatMap((child) => normalizedList(child));\n};\n\nconst convertFacts = (\n facts: readonly NormalizedUIElement[],\n context: ConversionContext,\n): SlackBlock[] => {\n const fields = facts.map((fact) => {\n const label = asString(fact.props[\"label\"]);\n const value = asString(fact.props[\"value\"]);\n return {\n type: \"mrkdwn\" as const,\n text: clampText(\n `*${label}*\\n${value}`,\n FACT_FIELD_TEXT_CAP,\n \"Fact\",\n \"field\",\n context,\n ),\n };\n });\n const blocks: SlackBlock[] = [];\n for (let index = 0; index < fields.length; index += FACT_FIELD_CAP) {\n blocks.push({\n type: \"section\",\n fields: fields.slice(index, index + FACT_FIELD_CAP),\n });\n }\n return blocks;\n};\n\nconst convertActions = (\n elements: readonly NormalizedUIElement[],\n context: ConversionContext,\n): SlackBlock[] => {\n const converted = elements\n .map((element) => toActionElement(element, context))\n .filter((element): element is SlackActionElement => element !== undefined);\n const blocks: SlackBlock[] = [];\n for (let index = 0; index < converted.length; index += ACTIONS_ELEMENT_CAP) {\n blocks.push({\n type: \"actions\",\n elements: converted.slice(index, index + ACTIONS_ELEMENT_CAP),\n });\n }\n return blocks;\n};\n\nconst cardActionButtons = (\n props: Readonly<Record<string, unknown>>,\n context: ConversionContext,\n): SlackButtonElement[] => {\n const buttons: SlackButtonElement[] = [];\n for (const key of [\"confirm\", \"cancel\"] as const) {\n const footer = props[key];\n if (!isRecord(footer)) continue;\n buttons.push(\n buttonElement(\n asString(footer[\"label\"]),\n key === \"confirm\" ? \"primary\" : undefined,\n footer[\"$action\"],\n \"Card\",\n context,\n ),\n );\n }\n return buttons;\n};\n\nconst cardTitleText = (\n props: Readonly<Record<string, unknown>>,\n context: ConversionContext,\n): string | undefined => {\n const rawTitle = asString(props[\"title\"]);\n return rawTitle\n ? clampText(rawTitle, CARD_TITLE_CAP, \"Card\", \"title\", context)\n : undefined;\n};\n\nconst isCardNoOp = (node: NormalizedUINode): boolean =>\n isElement(node) && (node.type === \"Spacer\" || node.type === \"Icon\");\n\nconst collectText = (\n node: NormalizedUINode | undefined,\n depth: number,\n): string[] => {\n if (node === undefined || node === null || depth > MAX_TRAVERSAL_DEPTH) {\n return [];\n }\n if (typeof node === \"string\" || typeof node === \"number\") {\n return [String(node)];\n }\n if (Array.isArray(node)) {\n return node.flatMap((child) => collectText(child, depth + 1));\n }\n const element = node as NormalizedUIElement;\n const own = [\"text\", \"value\", \"label\", \"title\", \"description\"]\n .map((key) => element.props[key])\n .filter((value): value is string => typeof value === \"string\");\n return [...own, ...collectText(element.children, depth + 1)];\n};\n\ntype CardFields = {\n readonly heroImage: SlackImageBlock | undefined;\n readonly bodyRaw: string | undefined;\n readonly subtextRaw: string | undefined;\n readonly leftover: readonly NormalizedUINode[];\n};\n\n/**\n * Scans a card's children for the pieces the native card block shape can\n * carry (a hero image, body text, and subtext), each taken from the first\n * matching child. Anything left over means the card cannot map cleanly onto\n * the native shape.\n */\nconst extractCardFields = (element: NormalizedUIElement): CardFields => {\n const children = normalizedList(element.children);\n let heroImage: SlackImageBlock | undefined;\n let bodyRaw: string | undefined;\n let subtextRaw: string | undefined;\n const leftover: NormalizedUINode[] = [];\n for (const child of children) {\n if (heroImage === undefined && isElement(child) && child.type === \"Image\") {\n heroImage = {\n type: \"image\",\n image_url: asString(child.props[\"src\"]),\n alt_text: asString(child.props[\"alt\"]),\n };\n continue;\n }\n if (\n bodyRaw === undefined &&\n isElement(child) &&\n (child.type === \"Text\" || child.type === \"Markdown\")\n ) {\n bodyRaw = asString(child.props[\"value\"]);\n continue;\n }\n if (\n subtextRaw === undefined &&\n isElement(child) &&\n child.type === \"Caption\"\n ) {\n subtextRaw = asString(child.props[\"value\"]);\n continue;\n }\n if (isCardNoOp(child)) continue;\n leftover.push(child);\n }\n return { heroImage, bodyRaw, subtextRaw, leftover };\n};\n\n/** Assembles a native card block, clamping its body and subtext text. */\nconst assembleCleanCard = (\n titleText: string | undefined,\n fields: CardFields,\n buttons: readonly SlackButtonElement[],\n context: ConversionContext,\n): SlackCardBlock => {\n const body: SlackTextObject | undefined =\n fields.bodyRaw !== undefined\n ? {\n type: \"mrkdwn\",\n text: clampText(\n fields.bodyRaw,\n CARD_BODY_CAP,\n \"Card\",\n \"body\",\n context,\n ),\n }\n : undefined;\n const subtext: SlackTextObject | undefined =\n fields.subtextRaw !== undefined\n ? {\n type: \"mrkdwn\",\n text: clampText(\n fields.subtextRaw,\n CARD_SUBTEXT_CAP,\n \"Card\",\n \"subtext\",\n context,\n ),\n }\n : undefined;\n if (buttons.length > CARD_ACTIONS_CAP) {\n warn(\n context,\n \"clamped\",\n \"Card\",\n `actions were clamped to ${CARD_ACTIONS_CAP} entries.`,\n );\n }\n return buildCardBlock({\n ...(fields.heroImage !== undefined ? { heroImage: fields.heroImage } : {}),\n ...(titleText !== undefined\n ? { title: { type: \"mrkdwn\", text: titleText } }\n : {}),\n ...(body !== undefined ? { body } : {}),\n ...(subtext !== undefined ? { subtext } : {}),\n ...(buttons.length > 0\n ? { actions: buttons.slice(0, CARD_ACTIONS_CAP) }\n : {}),\n });\n};\n\n/**\n * A card requires at least one of `hero_image`, `title`, `body`, or `actions`\n * (see {@link SlackCardBlock}); a card with none of them is not a valid\n * native block and must be dropped instead of emitted.\n */\nconst isEmptyCard = (card: SlackCardBlock): boolean =>\n card.hero_image === undefined &&\n card.title === undefined &&\n card.body === undefined &&\n (card.actions === undefined || card.actions.length === 0);\n\nconst convertCard = (\n element: NormalizedUIElement,\n context: ConversionContext,\n depth: number,\n): SlackBlock[] => {\n const titleText = cardTitleText(element.props, context);\n const fields = extractCardFields(element);\n const buttons = cardActionButtons(element.props, context);\n\n if (fields.leftover.length > 0) {\n warn(\n context,\n \"fallback\",\n \"Card\",\n \"A card with content beyond hero_image, body, subtext, and actions was rendered inline.\",\n );\n return [\n ...(titleText\n ? [{ type: \"header\" as const, text: plainText(titleText) }]\n : []),\n ...convertSequence(element.children, context, depth + 1),\n ...(buttons.length > 0\n ? [{ type: \"actions\" as const, elements: buttons }]\n : []),\n ];\n }\n const card = assembleCleanCard(titleText, fields, buttons, context);\n if (isEmptyCard(card)) {\n warn(\n context,\n \"dropped\",\n \"Card\",\n \"A card with none of hero_image, title, body, or actions was dropped.\",\n );\n return [];\n }\n return [card];\n};\n\n/**\n * Degrades a card that cannot map cleanly into a title-and-body card block.\n * A carousel cannot fall back to a block sequence like a standalone card\n * can, so its content is clamped down to fit the card shape instead.\n */\nconst degradeCard = (\n element: NormalizedUIElement,\n context: ConversionContext,\n depth: number,\n): SlackCardBlock => {\n const rawTitle = asString(element.props[\"title\"]);\n const textChunks = collectText(element.children, depth + 1);\n const titleSource = rawTitle || textChunks[0] || \"\";\n const bodyChunks = rawTitle ? textChunks : textChunks.slice(1);\n const titleText = titleSource.slice(0, CARD_TITLE_CAP);\n const bodyText = bodyChunks.join(\"\\n\").slice(0, CARD_BODY_CAP);\n warn(\n context,\n \"clamped\",\n \"Card\",\n \"A card inside a carousel was degraded to title and body.\",\n );\n return buildCardBlock({\n ...(titleText ? { title: { type: \"mrkdwn\", text: titleText } } : {}),\n ...(bodyText ? { body: { type: \"mrkdwn\", text: bodyText } } : {}),\n });\n};\n\nconst convertCarouselCard = (\n element: NormalizedUIElement,\n context: ConversionContext,\n depth: number,\n): SlackCardBlock | undefined => {\n const fields = extractCardFields(element);\n const card =\n fields.leftover.length === 0\n ? assembleCleanCard(\n cardTitleText(element.props, context),\n fields,\n cardActionButtons(element.props, context),\n context,\n )\n : degradeCard(element, context, depth);\n if (isEmptyCard(card)) {\n warn(\n context,\n \"dropped\",\n \"Card\",\n \"A card with none of hero_image, title, body, or actions was dropped.\",\n );\n return undefined;\n }\n return card;\n};\n\nconst isBadgeOrCaption = (\n node: NormalizedUINode,\n): node is NormalizedUIElement =>\n isElement(node) && (node.type === \"Badge\" || node.type === \"Caption\");\n\nconst contextRow = (\n element: NormalizedUIElement,\n context: ConversionContext,\n): SlackBlock[] | undefined => {\n const allChildren = normalizedList(element.children);\n const children = allChildren.filter(isBadgeOrCaption);\n if (allChildren.length === 0 || children.length !== allChildren.length) {\n return undefined;\n }\n if (children.length > CONTEXT_ELEMENT_CAP) {\n warn(\n context,\n \"clamped\",\n \"Row\",\n `context elements were clamped to ${CONTEXT_ELEMENT_CAP} entries.`,\n );\n }\n return [\n {\n type: \"context\",\n elements: children.slice(0, CONTEXT_ELEMENT_CAP).map((child) => ({\n type: \"mrkdwn\",\n text: clampText(\n asString(child.props[\"value\"]),\n CONTEXT_TEXT_CAP,\n child.type,\n \"value\",\n context,\n ),\n })),\n },\n ];\n};\n\nconst convertListItem = (\n element: NormalizedUIElement,\n context: ConversionContext,\n depth: number,\n): SlackBlock => {\n const text = clampText(\n collectText(element.children, depth + 1).join(\"\\n\"),\n SECTION_TEXT_CAP,\n \"ListViewItem\",\n \"text\",\n context,\n );\n const accessory = isRecord(element.action)\n ? buttonElement(\"Open\", undefined, element.action, \"ListViewItem\", context)\n : undefined;\n return {\n type: \"section\",\n text: { type: \"mrkdwn\", text },\n ...(accessory !== undefined ? { accessory } : {}),\n };\n};\n\nconst convertListView = (\n element: NormalizedUIElement,\n context: ConversionContext,\n depth: number,\n): SlackBlock[] => {\n const items = normalizedList(element.children).filter(\n (child): child is NormalizedUIElement =>\n isElement(child) && child.type === \"ListViewItem\",\n );\n return items.flatMap((item, index) => [\n ...(index > 0 ? [{ type: \"divider\" as const }] : []),\n convertListItem(item, context, depth + 1),\n ]);\n};\n\nconst convertCarousel = (\n element: NormalizedUIElement,\n context: ConversionContext,\n depth: number,\n): SlackBlock[] => {\n const cardChildren: NormalizedUIElement[] = [];\n for (const child of normalizedList(element.children)) {\n if (isElement(child) && child.type === \"Card\") {\n cardChildren.push(child);\n } else {\n warn(context, \"dropped\", \"Carousel\", \"A non-card child was dropped.\");\n }\n }\n if (cardChildren.length > CAROUSEL_CARD_CAP) {\n warn(\n context,\n \"clamped\",\n \"Carousel\",\n `cards were clamped to ${CAROUSEL_CARD_CAP} entries.`,\n );\n }\n const cards = cardChildren\n .slice(0, CAROUSEL_CARD_CAP)\n .map((card) => convertCarouselCard(card, context, depth + 1))\n .filter((card): card is SlackCardBlock => card !== undefined);\n if (cards.length < CAROUSEL_CARD_MIN) {\n warn(\n context,\n \"dropped\",\n \"Carousel\",\n \"A carousel without renderable cards was dropped.\",\n );\n return [];\n }\n return [buildCarouselBlock(cards)];\n};\n\nconst toDataTableCell = (value: unknown): SlackDataTableCell | undefined => {\n if (typeof value === \"number\") {\n return { type: \"raw_number\", text: String(value) };\n }\n if (typeof value === \"string\" || typeof value === \"boolean\") {\n return { type: \"raw_text\", text: String(value) };\n }\n return undefined;\n};\n\nconst rowCharacters = (row: readonly SlackDataTableCell[]): number =>\n row.reduce((sum, cell) => sum + cell.text.length, 0);\n\nconst convertTable = (\n element: NormalizedUIElement,\n context: ConversionContext,\n): SlackBlock[] => {\n const rawColumns = Array.isArray(element.props[\"columns\"])\n ? element.props[\"columns\"]\n : [];\n const rawRows = Array.isArray(element.props[\"rows\"])\n ? element.props[\"rows\"]\n : [];\n if (rawColumns.length > DATA_TABLE_COLUMN_CAP) {\n warn(\n context,\n \"clamped\",\n \"Table\",\n `columns were clamped to ${DATA_TABLE_COLUMN_CAP} entries.`,\n );\n }\n if (rawRows.length > DATA_TABLE_ROW_CAP) {\n warn(\n context,\n \"clamped\",\n \"Table\",\n `rows were clamped to ${DATA_TABLE_ROW_CAP} entries.`,\n );\n }\n\n const columnHeaderRow: SlackDataTableCell[] = rawColumns\n .slice(0, DATA_TABLE_COLUMN_CAP)\n .filter(isRecord)\n .map((column) => ({\n type: \"raw_text\" as const,\n text: asString(column[\"label\"]),\n }));\n const dataRows: SlackDataTableCell[][] = rawRows\n .slice(0, DATA_TABLE_ROW_CAP)\n .map((row) =>\n (Array.isArray(row) ? row : [])\n .slice(0, DATA_TABLE_COLUMN_CAP)\n .map(\n (value) =>\n toDataTableCell(value) ?? { type: \"raw_text\" as const, text: \"\" },\n ),\n );\n const width = Math.max(\n columnHeaderRow.length,\n 0,\n ...dataRows.map((row) => row.length),\n );\n const padRow = (row: SlackDataTableCell[]): SlackDataTableCell[] =>\n row.length >= width\n ? row\n : [\n ...row,\n ...Array.from({ length: width - row.length }, () => ({\n type: \"raw_text\" as const,\n text: \"\",\n })),\n ];\n const headerRow = padRow(columnHeaderRow);\n\n const headerCharacters = rowCharacters(headerRow);\n if (context.dataTableCharacters + headerCharacters > DATA_TABLE_CHAR_BUDGET) {\n warn(\n context,\n \"dropped\",\n \"Table\",\n `the table was dropped because its header row exceeds the ${DATA_TABLE_CHAR_BUDGET}-character table budget.`,\n );\n return [];\n }\n\n const kept: SlackDataTableCell[][] = [headerRow];\n let tableCharacters = headerCharacters;\n let budgetClamped = false;\n for (const row of dataRows.map(padRow)) {\n const chars = rowCharacters(row);\n if (\n context.dataTableCharacters + tableCharacters + chars >\n DATA_TABLE_CHAR_BUDGET\n ) {\n budgetClamped = true;\n break;\n }\n kept.push(row);\n tableCharacters += chars;\n }\n context.dataTableCharacters += tableCharacters;\n if (budgetClamped) {\n warn(\n context,\n \"clamped\",\n \"Table\",\n `rows were clamped to fit the ${DATA_TABLE_CHAR_BUDGET}-character table budget.`,\n );\n }\n\n return [buildDataTableBlock(kept)];\n};\n\nconst convertElement = (\n element: NormalizedUIElement,\n context: ConversionContext,\n depth: number,\n): SlackBlock[] => {\n const { props } = element;\n switch (element.type) {\n case \"Header\":\n return [\n {\n type: \"header\",\n text: plainText(\n clampText(\n asString(props[\"text\"]),\n HEADER_TEXT_CAP,\n \"Header\",\n \"text\",\n context,\n ),\n ),\n },\n ];\n case \"Text\":\n return [\n {\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: clampText(\n asString(props[\"value\"]),\n SECTION_TEXT_CAP,\n \"Text\",\n \"value\",\n context,\n ),\n },\n },\n ];\n case \"Caption\":\n case \"Badge\":\n return [\n {\n type: \"context\",\n elements: [\n {\n type: \"mrkdwn\",\n text: clampText(\n asString(props[\"value\"]),\n CONTEXT_TEXT_CAP,\n element.type,\n \"value\",\n context,\n ),\n },\n ],\n },\n ];\n case \"Fact\":\n return convertFacts([element], context);\n case \"Image\":\n return [\n {\n type: \"image\",\n image_url: asString(props[\"src\"]),\n alt_text: asString(props[\"alt\"]),\n },\n ];\n case \"Divider\":\n return [{ type: \"divider\" }];\n case \"Button\":\n case \"Select\":\n case \"DatePicker\":\n case \"Checkbox\":\n case \"RadioGroup\":\n return convertActions([element], context);\n case \"Input\": {\n const label = clampText(\n asString(props[\"label\"]),\n INPUT_LABEL_CAP,\n \"Input\",\n \"label\",\n context,\n );\n const placeholder = clampText(\n asString(props[\"placeholder\"]),\n PLACEHOLDER_TEXT_CAP,\n \"Input\",\n \"placeholder\",\n context,\n );\n return [\n {\n type: \"input\",\n label: plainText(label),\n element: {\n type: \"plain_text_input\",\n action_id: asActionId(element.action, \"Input\", context),\n ...(props[\"multiline\"] === true ? { multiline: true } : {}),\n ...(placeholder ? { placeholder: plainText(placeholder) } : {}),\n },\n },\n ];\n }\n case \"Alert\": {\n const tone =\n props[\"tone\"] === \"success\" ||\n props[\"tone\"] === \"warning\" ||\n props[\"tone\"] === \"danger\"\n ? props[\"tone\"]\n : \"info\";\n const titleText = asString(props[\"title\"]);\n const description = asString(props[\"description\"]);\n if (context.surface === \"modal\") {\n const combined = [titleText, description].filter(Boolean).join(\"\\n\");\n return [\n buildAlertBlock(\n clampText(combined, ALERT_TEXT_CAP, \"Alert\", \"text\", context),\n tone,\n ),\n ];\n }\n warn(\n context,\n \"fallback\",\n \"Alert\",\n \"Alert was rendered with message-surface fallback blocks.\",\n );\n return [\n ...buildAlertFallback(\n titleText,\n clampText(\n description,\n SECTION_TEXT_CAP,\n \"Alert\",\n \"description\",\n context,\n ),\n tone,\n ),\n ];\n }\n case \"Card\":\n return convertCard(element, context, depth);\n case \"Carousel\":\n return convertCarousel(element, context, depth);\n case \"Col\":\n case \"Box\":\n return convertSequence(element.children, context, depth + 1);\n case \"Row\":\n return (\n contextRow(element, context) ??\n convertSequence(element.children, context, depth + 1)\n );\n case \"Spacer\":\n case \"Icon\":\n return [];\n case \"Table\":\n return convertTable(element, context);\n case \"Markdown\": {\n const value = asString(props[\"value\"]);\n if (\n !context.markdownExhausted &&\n context.markdownCharacters + value.length <= MARKDOWN_TEXT_BUDGET\n ) {\n context.markdownCharacters += value.length;\n return [{ type: \"markdown\", text: value }];\n }\n context.markdownExhausted = true;\n warn(\n context,\n \"fallback\",\n \"Markdown\",\n `The ${MARKDOWN_TEXT_BUDGET}-character markdown budget was exceeded.`,\n );\n return [\n {\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: clampText(\n value,\n SECTION_TEXT_CAP,\n \"Markdown\",\n \"fallback text\",\n context,\n ),\n },\n },\n ];\n }\n case \"Chart\":\n warn(\n context,\n \"dropped\",\n \"Chart\",\n \"Chart was replaced by a Slack omission note.\",\n );\n return [\n {\n type: \"context\",\n elements: [{ type: \"mrkdwn\", text: \"Chart omitted on Slack.\" }],\n },\n ];\n case \"ListView\":\n return convertListView(element, context, depth);\n case \"ListViewItem\":\n return [convertListItem(element, context, depth)];\n case \"Form\":\n return [\n ...convertSequence(element.children, context, depth + 1),\n {\n type: \"actions\",\n elements: [\n buttonElement(\"Submit\", \"primary\", element.action, \"Form\", context),\n ],\n },\n ];\n default:\n warn(\n context,\n \"dropped\",\n element.type,\n \"Unknown component type was dropped.\",\n );\n return [];\n }\n};\n\nfunction convertSequence(\n node: NormalizedUINode | undefined,\n context: ConversionContext,\n depth: number,\n): SlackBlock[] {\n if (node === undefined || node === null || depth > MAX_TRAVERSAL_DEPTH) {\n return [];\n }\n const nodes = Array.isArray(node) ? node : [node];\n const blocks: SlackBlock[] = [];\n for (let index = 0; index < nodes.length;) {\n const current = nodes[index];\n if (current === undefined || current === null) {\n index += 1;\n continue;\n }\n if (isElement(current) && current.type === \"Fact\") {\n const facts: NormalizedUIElement[] = [];\n while (index < nodes.length) {\n const candidate = nodes[index];\n if (!candidate || !isElement(candidate) || candidate.type !== \"Fact\") {\n break;\n }\n facts.push(candidate);\n index += 1;\n }\n blocks.push(...convertFacts(facts, context));\n continue;\n }\n if (isElement(current) && INTERACTIVE_TYPES.has(current.type)) {\n const controls: NormalizedUIElement[] = [];\n while (index < nodes.length) {\n const candidate = nodes[index];\n if (\n !candidate ||\n !isElement(candidate) ||\n !INTERACTIVE_TYPES.has(candidate.type)\n ) {\n break;\n }\n controls.push(candidate);\n index += 1;\n }\n blocks.push(...convertActions(controls, context));\n continue;\n }\n if (typeof current === \"string\" || typeof current === \"number\") {\n blocks.push({\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: clampText(\n String(current),\n SECTION_TEXT_CAP,\n \"Text\",\n \"value\",\n context,\n ),\n },\n });\n } else if (Array.isArray(current)) {\n blocks.push(...convertSequence(current, context, depth + 1));\n } else {\n blocks.push(\n ...convertElement(current as NormalizedUIElement, context, depth + 1),\n );\n }\n index += 1;\n }\n return blocks;\n}\n\ninterface BoundState {\n remaining: number;\n exhausted: boolean;\n}\n\ntype ClampReason = \"children\" | \"budget\" | \"cycle\" | \"depth\";\n\nfunction boundNode(\n value: unknown,\n depth: number,\n onClamp: (reason: ClampReason) => void,\n state: BoundState,\n ancestors: WeakSet<object>,\n): unknown {\n if (state.remaining <= 0) {\n if (!state.exhausted) {\n state.exhausted = true;\n onClamp(\"budget\");\n }\n return null;\n }\n state.remaining -= 1;\n if (depth > MAX_TRAVERSAL_DEPTH) {\n onClamp(\"depth\");\n return null;\n }\n if (Array.isArray(value)) {\n if (ancestors.has(value)) {\n onClamp(\"cycle\");\n return null;\n }\n ancestors.add(value);\n const bounded = Array.prototype.slice.call(\n value,\n 0,\n CHILDREN_CAP,\n ) as unknown[];\n if (value.length > CHILDREN_CAP) onClamp(\"children\");\n const result = bounded.map((item) =>\n boundNode(item, depth + 1, onClamp, state, ancestors),\n );\n ancestors.delete(value);\n return result;\n }\n if (\n value !== null &&\n typeof value === \"object\" &&\n \"children\" in (value as Record<string, unknown>)\n ) {\n if (ancestors.has(value)) {\n onClamp(\"cycle\");\n return null;\n }\n ancestors.add(value);\n const record = value as Record<string, unknown>;\n const result = {\n ...record,\n children: boundNode(\n record[\"children\"],\n depth + 1,\n onClamp,\n state,\n ancestors,\n ),\n };\n ancestors.delete(value);\n return result;\n }\n return value;\n}\n\n/**\n * Produces a bounded plain copy of a raw generative-ui spec before it\n * reaches `normalizeSpec`, whose own traversal of the root array or any\n * `children` array walks the full reported length of a hostile proxied\n * array before any per-field cap downstream ever applies. Every array\n * (root, or `children` at any depth) is capped to {@link CHILDREN_CAP}\n * entries via `Array.prototype.slice`, which bounds even a proxy with a\n * fabricated `length`; recursion itself is capped at\n * {@link MAX_TRAVERSAL_DEPTH}. `onClamp` fires once per level that was\n * truncated, receiving the reason for that truncation: `\"children\"`,\n * `\"depth\"`, `\"budget\"`, or `\"cycle\"`. The walk also spends a total budget of\n * {@link NODE_BUDGET} nodes, so shared references cannot multiply work\n * exponentially, and a node that is its own ancestor is cut to `null`.\n */\nfunction boundSpec(\n spec: unknown,\n onClamp: (reason: ClampReason) => void,\n): unknown {\n return boundNode(\n spec,\n 0,\n onClamp,\n { remaining: NODE_BUDGET, exhausted: false },\n new WeakSet(),\n );\n}\n\n/** Converts a generative-UI tree into Slack Block Kit JSON and downgrade warnings. */\nexport function toSlackBlocks(\n node: unknown,\n options?: ToSlackBlocksOptions,\n): SlackBlocksResult {\n const surface = options?.surface === \"modal\" ? \"modal\" : \"message\";\n const context: ConversionContext = {\n blockCap: surface === \"modal\" ? MODAL_BLOCK_CAP : MESSAGE_BLOCK_CAP,\n surface,\n warnings: [],\n markdownCharacters: 0,\n markdownExhausted: false,\n dataTableCharacters: 0,\n };\n try {\n const bounded = boundSpec(node, (reason) => {\n const detail =\n reason === \"budget\"\n ? `the tree was truncated after ${NODE_BUDGET} nodes.`\n : reason === \"cycle\"\n ? \"a self-referencing node was dropped.\"\n : reason === \"depth\"\n ? `nodes deeper than ${MAX_TRAVERSAL_DEPTH} levels were dropped.`\n : `children were clamped to ${CHILDREN_CAP} entries.`;\n warn(context, \"clamped\", \"Root\", detail);\n });\n const { root } = normalizeSpec(bounded as never);\n const converted = convertSequence(root, context, 0);\n if (converted.length <= context.blockCap) {\n return { blocks: converted, warnings: context.warnings };\n }\n const kept = converted.slice(0, context.blockCap - 1);\n const omitted = converted.length - kept.length;\n warn(\n context,\n \"clamped\",\n \"Root\",\n `${omitted} ${omitted === 1 ? \"block was\" : \"blocks were\"} omitted.`,\n );\n return {\n blocks: [...kept, buildBlockBudgetNote(omitted)],\n warnings: context.warnings,\n };\n } catch {\n return {\n blocks: [],\n warnings: [\n {\n code: \"dropped\",\n component: \"Root\",\n detail: \"Input could not be converted.\",\n },\n ],\n };\n }\n}\n"],"mappings":";;;AAmEA,MAAM,oCAAoB,IAAI,IAAI;CAChC;CACA;CACA;CACA;CACA;AACF,CAAC;AAED,MAAM,eAAe;AAErB,MAAM,YAAY,UAChB,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAErE,MAAM,aAAa,SACjB,SAAS,IAAI;AAEf,MAAM,YAAY,UAChB,OAAO,UAAU,WAAW,QAAQ;AAEtC,MAAM,QACJ,SACA,MACA,WACA,WACG;CACH,QAAQ,SAAS,KAAK;EAAE;EAAM;EAAW;CAAO,CAAC;AACnD;AAEA,MAAM,aACJ,OACA,KACA,WACA,OACA,YACW;CACX,IAAI,MAAM,UAAU,KAAK,OAAO;CAChC,KACE,SACA,WACA,WACA,GAAG,MAAM,kBAAkB,IAAI,aACjC;CACA,OAAO,MAAM,MAAM,GAAG,GAAG;AAC3B;AAEA,MAAM,cACJ,QACA,WACA,YAEA,UACE,SAAS,MAAM,KAAK,OAAO,OAAO,YAAY,WAC1C,OAAO,UACP,UAAA,KAEJ,WACA,aACA,OACF;AAEF,MAAM,aAAa,UAAkC;CACnD,MAAM;CACN;AACF;AAEA,MAAM,eAAe,WAAwC;CAC3D,IAAI,CAAC,SAAS,MAAM,GAAG,OAAO,KAAA;CAC9B,MAAM,EAAE,MAAM,OAAO,GAAG,YAAY;CACpC,IAAI,OAAO,KAAK,OAAO,CAAC,CAAC,WAAW,GAAG,OAAO,KAAA;CAC9C,IAAI;EACF,MAAM,aAAa,KAAK,UAAU,OAAO;EACzC,OAAO,eAAe,OAAO,KAAA,IAAY;CAC3C,QAAQ;EACN;CACF;AACF;AAEA,MAAM,iBACJ,OACA,aACA,QACA,WACA,YACuB;CAEvB,IAAI,QADoB,YAAY,MACV;CAC1B,IAAI,UAAU,KAAA,KAAa,MAAM,SAAA,KAA2B;EAC1D,KACE,SACA,WACA,WACA,2EACF;EACA,QAAQ,KAAA;CACV;CACA,MAAM,QACJ,gBAAgB,aAAa,gBAAgB,WACzC,cACA,KAAA;CACN,OAAO;EACL,MAAM;EACN,MAAM,UACJ,UAAU,OAAA,IAA6B,WAAW,SAAS,OAAO,CACpE;EACA,WAAW,WAAW,QAAQ,WAAW,OAAO;EAChD,GAAI,UAAU,KAAA,IAAY,EAAE,MAAM,IAAI,CAAC;EACvC,GAAI,UAAU,KAAA,IAAY,EAAE,MAAM,IAAI,CAAC;CACzC;AACF;AAEA,MAAM,cACJ,OACA,WACA,YAC4B;CAC5B,IACE,CAAC,SAAS,KAAK,KACf,OAAO,MAAM,aAAa,YAC1B,OAAO,MAAM,aAAa,UAE1B;CAEF,OAAO;EACL,MAAM,UACJ,UACE,MAAM,UAAA,IAEN,WACA,gBACA,OACF,CACF;EACA,OAAO,MAAM;CACf;AACF;AAEA,MAAM,mBACJ,SACA,YACmC;CACnC,MAAM,EAAE,OAAO,WAAW;CAC1B,QAAQ,QAAQ,MAAhB;EACE,KAAK,UACH,OAAO,cACL,SAAS,MAAM,QAAQ,GACvB,MAAM,gBACN,QACA,UACA,OACF;EACF,KAAK,UAAU;GACb,MAAM,aAAa,MAAM,QAAQ,MAAM,UAAU,IAC7C,MAAM,aACN,CAAC;GACL,IAAI,WAAW,SAAA,KACb,KACE,SACA,WACA,UACA,sCACF;GAEF,MAAM,UAAU,WACb,MAAM,GAAA,GAAoB,CAAC,CAC3B,KAAK,WAAW,WAAW,QAAQ,UAAU,OAAO,CAAC,CAAC,CACtD,QAAQ,WAAkC,WAAW,KAAA,CAAS;GACjE,MAAM,cAAc,UAClB,SAAS,MAAM,cAAc,GAAA,KAE7B,UACA,eACA,OACF;GACA,OAAO;IACL,MAAM;IACN,WAAW,WAAW,QAAQ,UAAU,OAAO;IAC/C;IACA,GAAI,cAAc,EAAE,aAAa,UAAU,WAAW,EAAE,IAAI,CAAC;GAC/D;EACF;EACA,KAAK,cAAc;GACjB,MAAM,UAAU,SAAS,MAAM,QAAQ;GACvC,MAAM,cAAc,aAAa,KAAK,OAAO,IAAI,UAAU,KAAA;GAC3D,IAAI,WAAW,gBAAgB,KAAA,GAC7B,KACE,SACA,WACA,cACA,4DACF;GAEF,OAAO;IACL,MAAM;IACN,WAAW,WAAW,QAAQ,cAAc,OAAO;IACnD,GAAI,gBAAgB,KAAA,IAAY,EAAE,cAAc,YAAY,IAAI,CAAC;GACnE;EACF;EACA,KAAK,YAAY;GACf,MAAM,QAAQ,UACZ,SAAS,MAAM,QAAQ,GAAA,IAEvB,YACA,SACA,OACF;GACA,MAAM,OAAO,SAAS,MAAM,OAAO;GACnC,MAAM,SAAsB;IAC1B,MAAM,UAAU,KAAK;IACrB,OAAO,QAAQ;GACjB;GACA,OAAO;IACL,MAAM;IACN,WAAW,WAAW,QAAQ,YAAY,OAAO;IACjD,SAAS,CAAC,MAAM;IAChB,GAAI,MAAM,sBAAsB,OAC5B,EAAE,iBAAiB,CAAC,MAAM,EAAE,IAC5B,CAAC;GACP;EACF;EACA,KAAK,cAAc;GACjB,MAAM,aAAa,MAAM,QAAQ,MAAM,UAAU,IAC7C,MAAM,aACN,CAAC;GACL,IAAI,WAAW,SAAA,IACb,KACE,SACA,WACA,cACA,qCACF;GAEF,MAAM,UAAU,WACb,MAAM,GAAA,EAAmB,CAAC,CAC1B,KAAK,WAAW,WAAW,QAAQ,cAAc,OAAO,CAAC,CAAC,CAC1D,QAAQ,WAAkC,WAAW,KAAA,CAAS;GACjE,MAAM,gBACJ,OAAO,MAAM,aAAa,WACtB,MAAM,WACN,SAAS,MAAM,eAAe;GACpC,MAAM,gBAAgB,QAAQ,MAC3B,WAAW,OAAO,UAAU,aAC/B;GACA,OAAO;IACL,MAAM;IACN,WAAW,WAAW,QAAQ,cAAc,OAAO;IACnD;IACA,GAAI,kBAAkB,KAAA,IAClB,EAAE,gBAAgB,cAAc,IAChC,CAAC;GACP;EACF;EACA,SACE;CACJ;AACF;AAEA,MAAM,kBACJ,SACuB;CACvB,IAAI,SAAS,KAAA,KAAa,SAAS,MAAM,OAAO,CAAC;CACjD,IAAI,CAAC,MAAM,QAAQ,IAAI,GAAG,OAAO,CAAC,IAAI;CACtC,OAAO,KAAK,SAAS,UAAU,eAAe,KAAK,CAAC;AACtD;AAEA,MAAM,gBACJ,OACA,YACiB;CACjB,MAAM,SAAS,MAAM,KAAK,SAAS;EACjC,MAAM,QAAQ,SAAS,KAAK,MAAM,QAAQ;EAC1C,MAAM,QAAQ,SAAS,KAAK,MAAM,QAAQ;EAC1C,OAAO;GACL,MAAM;GACN,MAAM,UACJ,IAAI,MAAM,KAAK,SACf,qBACA,QACA,SACA,OACF;EACF;CACF,CAAC;CACD,MAAM,SAAuB,CAAC;CAC9B,KAAK,IAAI,QAAQ,GAAG,QAAQ,OAAO,QAAQ,SAAA,IACzC,OAAO,KAAK;EACV,MAAM;EACN,QAAQ,OAAO,MAAM,OAAO,QAAA,EAAsB;CACpD,CAAC;CAEH,OAAO;AACT;AAEA,MAAM,kBACJ,UACA,YACiB;CACjB,MAAM,YAAY,SACf,KAAK,YAAY,gBAAgB,SAAS,OAAO,CAAC,CAAC,CACnD,QAAQ,YAA2C,YAAY,KAAA,CAAS;CAC3E,MAAM,SAAuB,CAAC;CAC9B,KAAK,IAAI,QAAQ,GAAG,QAAQ,UAAU,QAAQ,SAAA,IAC5C,OAAO,KAAK;EACV,MAAM;EACN,UAAU,UAAU,MAAM,OAAO,QAAA,EAA2B;CAC9D,CAAC;CAEH,OAAO;AACT;AAEA,MAAM,qBACJ,OACA,YACyB;CACzB,MAAM,UAAgC,CAAC;CACvC,KAAK,MAAM,OAAO,CAAC,WAAW,QAAQ,GAAY;EAChD,MAAM,SAAS,MAAM;EACrB,IAAI,CAAC,SAAS,MAAM,GAAG;EACvB,QAAQ,KACN,cACE,SAAS,OAAO,QAAQ,GACxB,QAAQ,YAAY,YAAY,KAAA,GAChC,OAAO,YACP,QACA,OACF,CACF;CACF;CACA,OAAO;AACT;AAEA,MAAM,iBACJ,OACA,YACuB;CACvB,MAAM,WAAW,SAAS,MAAM,QAAQ;CACxC,OAAO,WACH,UAAU,UAAA,KAA0B,QAAQ,SAAS,OAAO,IAC5D,KAAA;AACN;AAEA,MAAM,cAAc,SAClB,UAAU,IAAI,MAAM,KAAK,SAAS,YAAY,KAAK,SAAS;AAE9D,MAAM,eACJ,MACA,UACa;CACb,IAAI,SAAS,KAAA,KAAa,SAAS,QAAQ,QAAA,IACzC,OAAO,CAAC;CAEV,IAAI,OAAO,SAAS,YAAY,OAAO,SAAS,UAC9C,OAAO,CAAC,OAAO,IAAI,CAAC;CAEtB,IAAI,MAAM,QAAQ,IAAI,GACpB,OAAO,KAAK,SAAS,UAAU,YAAY,OAAO,QAAQ,CAAC,CAAC;CAE9D,MAAM,UAAU;CAIhB,OAAO,CAAC,GAHI;EAAC;EAAQ;EAAS;EAAS;EAAS;CAAa,CAAC,CAC3D,KAAK,QAAQ,QAAQ,MAAM,IAAI,CAAC,CAChC,QAAQ,UAA2B,OAAO,UAAU,QAC1C,GAAG,GAAG,YAAY,QAAQ,UAAU,QAAQ,CAAC,CAAC;AAC7D;;;;;;;AAeA,MAAM,qBAAqB,YAA6C;CACtE,MAAM,WAAW,eAAe,QAAQ,QAAQ;CAChD,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,MAAM,WAA+B,CAAC;CACtC,KAAK,MAAM,SAAS,UAAU;EAC5B,IAAI,cAAc,KAAA,KAAa,UAAU,KAAK,KAAK,MAAM,SAAS,SAAS;GACzE,YAAY;IACV,MAAM;IACN,WAAW,SAAS,MAAM,MAAM,MAAM;IACtC,UAAU,SAAS,MAAM,MAAM,MAAM;GACvC;GACA;EACF;EACA,IACE,YAAY,KAAA,KACZ,UAAU,KAAK,MACd,MAAM,SAAS,UAAU,MAAM,SAAS,aACzC;GACA,UAAU,SAAS,MAAM,MAAM,QAAQ;GACvC;EACF;EACA,IACE,eAAe,KAAA,KACf,UAAU,KAAK,KACf,MAAM,SAAS,WACf;GACA,aAAa,SAAS,MAAM,MAAM,QAAQ;GAC1C;EACF;EACA,IAAI,WAAW,KAAK,GAAG;EACvB,SAAS,KAAK,KAAK;CACrB;CACA,OAAO;EAAE;EAAW;EAAS;EAAY;CAAS;AACpD;;AAGA,MAAM,qBACJ,WACA,QACA,SACA,YACmB;CACnB,MAAM,OACJ,OAAO,YAAY,KAAA,IACf;EACE,MAAM;EACN,MAAM,UACJ,OAAO,SAAA,KAEP,QACA,QACA,OACF;CACF,IACA,KAAA;CACN,MAAM,UACJ,OAAO,eAAe,KAAA,IAClB;EACE,MAAM;EACN,MAAM,UACJ,OAAO,YAAA,KAEP,QACA,WACA,OACF;CACF,IACA,KAAA;CACN,IAAI,QAAQ,SAAA,GACV,KACE,SACA,WACA,QACA,oCACF;CAEF,OAAO,eAAe;EACpB,GAAI,OAAO,cAAc,KAAA,IAAY,EAAE,WAAW,OAAO,UAAU,IAAI,CAAC;EACxE,GAAI,cAAc,KAAA,IACd,EAAE,OAAO;GAAE,MAAM;GAAU,MAAM;EAAU,EAAE,IAC7C,CAAC;EACL,GAAI,SAAS,KAAA,IAAY,EAAE,KAAK,IAAI,CAAC;EACrC,GAAI,YAAY,KAAA,IAAY,EAAE,QAAQ,IAAI,CAAC;EAC3C,GAAI,QAAQ,SAAS,IACjB,EAAE,SAAS,QAAQ,MAAM,GAAA,CAAmB,EAAE,IAC9C,CAAC;CACP,CAAC;AACH;;;;;;AAOA,MAAM,eAAe,SACnB,KAAK,eAAe,KAAA,KACpB,KAAK,UAAU,KAAA,KACf,KAAK,SAAS,KAAA,MACb,KAAK,YAAY,KAAA,KAAa,KAAK,QAAQ,WAAW;AAEzD,MAAM,eACJ,SACA,SACA,UACiB;CACjB,MAAM,YAAY,cAAc,QAAQ,OAAO,OAAO;CACtD,MAAM,SAAS,kBAAkB,OAAO;CACxC,MAAM,UAAU,kBAAkB,QAAQ,OAAO,OAAO;CAExD,IAAI,OAAO,SAAS,SAAS,GAAG;EAC9B,KACE,SACA,YACA,QACA,wFACF;EACA,OAAO;GACL,GAAI,YACA,CAAC;IAAE,MAAM;IAAmB,MAAM,UAAU,SAAS;GAAE,CAAC,IACxD,CAAC;GACL,GAAG,gBAAgB,QAAQ,UAAU,SAAS,QAAQ,CAAC;GACvD,GAAI,QAAQ,SAAS,IACjB,CAAC;IAAE,MAAM;IAAoB,UAAU;GAAQ,CAAC,IAChD,CAAC;EACP;CACF;CACA,MAAM,OAAO,kBAAkB,WAAW,QAAQ,SAAS,OAAO;CAClE,IAAI,YAAY,IAAI,GAAG;EACrB,KACE,SACA,WACA,QACA,sEACF;EACA,OAAO,CAAC;CACV;CACA,OAAO,CAAC,IAAI;AACd;;;;;;AAOA,MAAM,eACJ,SACA,SACA,UACmB;CACnB,MAAM,WAAW,SAAS,QAAQ,MAAM,QAAQ;CAChD,MAAM,aAAa,YAAY,QAAQ,UAAU,QAAQ,CAAC;CAC1D,MAAM,cAAc,YAAY,WAAW,MAAM;CACjD,MAAM,aAAa,WAAW,aAAa,WAAW,MAAM,CAAC;CAC7D,MAAM,YAAY,YAAY,MAAM,GAAA,GAAiB;CACrD,MAAM,WAAW,WAAW,KAAK,IAAI,CAAC,CAAC,MAAM,GAAA,GAAgB;CAC7D,KACE,SACA,WACA,QACA,0DACF;CACA,OAAO,eAAe;EACpB,GAAI,YAAY,EAAE,OAAO;GAAE,MAAM;GAAU,MAAM;EAAU,EAAE,IAAI,CAAC;EAClE,GAAI,WAAW,EAAE,MAAM;GAAE,MAAM;GAAU,MAAM;EAAS,EAAE,IAAI,CAAC;CACjE,CAAC;AACH;AAEA,MAAM,uBACJ,SACA,SACA,UAC+B;CAC/B,MAAM,SAAS,kBAAkB,OAAO;CACxC,MAAM,OACJ,OAAO,SAAS,WAAW,IACvB,kBACE,cAAc,QAAQ,OAAO,OAAO,GACpC,QACA,kBAAkB,QAAQ,OAAO,OAAO,GACxC,OACF,IACA,YAAY,SAAS,SAAS,KAAK;CACzC,IAAI,YAAY,IAAI,GAAG;EACrB,KACE,SACA,WACA,QACA,sEACF;EACA;CACF;CACA,OAAO;AACT;AAEA,MAAM,oBACJ,SAEA,UAAU,IAAI,MAAM,KAAK,SAAS,WAAW,KAAK,SAAS;AAE7D,MAAM,cACJ,SACA,YAC6B;CAC7B,MAAM,cAAc,eAAe,QAAQ,QAAQ;CACnD,MAAM,WAAW,YAAY,OAAO,gBAAgB;CACpD,IAAI,YAAY,WAAW,KAAK,SAAS,WAAW,YAAY,QAC9D;CAEF,IAAI,SAAS,SAAA,IACX,KACE,SACA,WACA,OACA,8CACF;CAEF,OAAO,CACL;EACE,MAAM;EACN,UAAU,SAAS,MAAM,GAAA,EAAsB,CAAC,CAAC,KAAK,WAAW;GAC/D,MAAM;GACN,MAAM,UACJ,SAAS,MAAM,MAAM,QAAQ,GAC7B,kBACA,MAAM,MACN,SACA,OACF;EACF,EAAE;CACJ,CACF;AACF;AAEA,MAAM,mBACJ,SACA,SACA,UACe;CACf,MAAM,OAAO,UACX,YAAY,QAAQ,UAAU,QAAQ,CAAC,CAAC,CAAC,KAAK,IAAI,GAClD,kBACA,gBACA,QACA,OACF;CACA,MAAM,YAAY,SAAS,QAAQ,MAAM,IACrC,cAAc,QAAQ,KAAA,GAAW,QAAQ,QAAQ,gBAAgB,OAAO,IACxE,KAAA;CACJ,OAAO;EACL,MAAM;EACN,MAAM;GAAE,MAAM;GAAU;EAAK;EAC7B,GAAI,cAAc,KAAA,IAAY,EAAE,UAAU,IAAI,CAAC;CACjD;AACF;AAEA,MAAM,mBACJ,SACA,SACA,UACiB;CAKjB,OAJc,eAAe,QAAQ,QAAQ,CAAC,CAAC,QAC5C,UACC,UAAU,KAAK,KAAK,MAAM,SAAS,cAE5B,CAAC,CAAC,SAAS,MAAM,UAAU,CACpC,GAAI,QAAQ,IAAI,CAAC,EAAE,MAAM,UAAmB,CAAC,IAAI,CAAC,GAClD,gBAAgB,MAAM,SAAS,QAAQ,CAAC,CAC1C,CAAC;AACH;AAEA,MAAM,mBACJ,SACA,SACA,UACiB;CACjB,MAAM,eAAsC,CAAC;CAC7C,KAAK,MAAM,SAAS,eAAe,QAAQ,QAAQ,GACjD,IAAI,UAAU,KAAK,KAAK,MAAM,SAAS,QACrC,aAAa,KAAK,KAAK;MAEvB,KAAK,SAAS,WAAW,YAAY,+BAA+B;CAGxE,IAAI,aAAa,SAAA,IACf,KACE,SACA,WACA,YACA,mCACF;CAEF,MAAM,QAAQ,aACX,MAAM,GAAA,EAAoB,CAAC,CAC3B,KAAK,SAAS,oBAAoB,MAAM,SAAS,QAAQ,CAAC,CAAC,CAAC,CAC5D,QAAQ,SAAiC,SAAS,KAAA,CAAS;CAC9D,IAAI,MAAM,SAAA,GAA4B;EACpC,KACE,SACA,WACA,YACA,kDACF;EACA,OAAO,CAAC;CACV;CACA,OAAO,CAAC,mBAAmB,KAAK,CAAC;AACnC;AAEA,MAAM,mBAAmB,UAAmD;CAC1E,IAAI,OAAO,UAAU,UACnB,OAAO;EAAE,MAAM;EAAc,MAAM,OAAO,KAAK;CAAE;CAEnD,IAAI,OAAO,UAAU,YAAY,OAAO,UAAU,WAChD,OAAO;EAAE,MAAM;EAAY,MAAM,OAAO,KAAK;CAAE;AAGnD;AAEA,MAAM,iBAAiB,QACrB,IAAI,QAAQ,KAAK,SAAS,MAAM,KAAK,KAAK,QAAQ,CAAC;AAErD,MAAM,gBACJ,SACA,YACiB;CACjB,MAAM,aAAa,MAAM,QAAQ,QAAQ,MAAM,UAAU,IACrD,QAAQ,MAAM,aACd,CAAC;CACL,MAAM,UAAU,MAAM,QAAQ,QAAQ,MAAM,OAAO,IAC/C,QAAQ,MAAM,UACd,CAAC;CACL,IAAI,WAAW,SAAA,IACb,KACE,SACA,WACA,SACA,qCACF;CAEF,IAAI,QAAQ,SAAA,KACV,KACE,SACA,WACA,SACA,mCACF;CAGF,MAAM,kBAAwC,WAC3C,MAAM,GAAA,EAAwB,CAAC,CAC/B,OAAO,QAAQ,CAAC,CAChB,KAAK,YAAY;EAChB,MAAM;EACN,MAAM,SAAS,OAAO,QAAQ;CAChC,EAAE;CACJ,MAAM,WAAmC,QACtC,MAAM,GAAA,GAAqB,CAAC,CAC5B,KAAK,SACH,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAC,EAAA,CAC1B,MAAM,GAAA,EAAwB,CAAC,CAC/B,KACE,UACC,gBAAgB,KAAK,KAAK;EAAE,MAAM;EAAqB,MAAM;CAAG,CACpE,CACJ;CACF,MAAM,QAAQ,KAAK,IACjB,gBAAgB,QAChB,GACA,GAAG,SAAS,KAAK,QAAQ,IAAI,MAAM,CACrC;CACA,MAAM,UAAU,QACd,IAAI,UAAU,QACV,MACA,CACE,GAAG,KACH,GAAG,MAAM,KAAK,EAAE,QAAQ,QAAQ,IAAI,OAAO,UAAU;EACnD,MAAM;EACN,MAAM;CACR,EAAE,CACJ;CACN,MAAM,YAAY,OAAO,eAAe;CAExC,MAAM,mBAAmB,cAAc,SAAS;CAChD,IAAI,QAAQ,sBAAsB,mBAAA,KAA2C;EAC3E,KACE,SACA,WACA,SACA,4DAA4D,uBAAuB,yBACrF;EACA,OAAO,CAAC;CACV;CAEA,MAAM,OAA+B,CAAC,SAAS;CAC/C,IAAI,kBAAkB;CACtB,IAAI,gBAAgB;CACpB,KAAK,MAAM,OAAO,SAAS,IAAI,MAAM,GAAG;EACtC,MAAM,QAAQ,cAAc,GAAG;EAC/B,IACE,QAAQ,sBAAsB,kBAAkB,QAAA,KAEhD;GACA,gBAAgB;GAChB;EACF;EACA,KAAK,KAAK,GAAG;EACb,mBAAmB;CACrB;CACA,QAAQ,uBAAuB;CAC/B,IAAI,eACF,KACE,SACA,WACA,SACA,gCAAgC,uBAAuB,yBACzD;CAGF,OAAO,CAAC,oBAAoB,IAAI,CAAC;AACnC;AAEA,MAAM,kBACJ,SACA,SACA,UACiB;CACjB,MAAM,EAAE,UAAU;CAClB,QAAQ,QAAQ,MAAhB;EACE,KAAK,UACH,OAAO,CACL;GACE,MAAM;GACN,MAAM,UACJ,UACE,SAAS,MAAM,OAAO,GAAA,KAEtB,UACA,QACA,OACF,CACF;EACF,CACF;EACF,KAAK,QACH,OAAO,CACL;GACE,MAAM;GACN,MAAM;IACJ,MAAM;IACN,MAAM,UACJ,SAAS,MAAM,QAAQ,GACvB,kBACA,QACA,SACA,OACF;GACF;EACF,CACF;EACF,KAAK;EACL,KAAK,SACH,OAAO,CACL;GACE,MAAM;GACN,UAAU,CACR;IACE,MAAM;IACN,MAAM,UACJ,SAAS,MAAM,QAAQ,GACvB,kBACA,QAAQ,MACR,SACA,OACF;GACF,CACF;EACF,CACF;EACF,KAAK,QACH,OAAO,aAAa,CAAC,OAAO,GAAG,OAAO;EACxC,KAAK,SACH,OAAO,CACL;GACE,MAAM;GACN,WAAW,SAAS,MAAM,MAAM;GAChC,UAAU,SAAS,MAAM,MAAM;EACjC,CACF;EACF,KAAK,WACH,OAAO,CAAC,EAAE,MAAM,UAAU,CAAC;EAC7B,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,cACH,OAAO,eAAe,CAAC,OAAO,GAAG,OAAO;EAC1C,KAAK,SAAS;GACZ,MAAM,QAAQ,UACZ,SAAS,MAAM,QAAQ,GACvB,iBACA,SACA,SACA,OACF;GACA,MAAM,cAAc,UAClB,SAAS,MAAM,cAAc,GAAA,KAE7B,SACA,eACA,OACF;GACA,OAAO,CACL;IACE,MAAM;IACN,OAAO,UAAU,KAAK;IACtB,SAAS;KACP,MAAM;KACN,WAAW,WAAW,QAAQ,QAAQ,SAAS,OAAO;KACtD,GAAI,MAAM,iBAAiB,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;KACzD,GAAI,cAAc,EAAE,aAAa,UAAU,WAAW,EAAE,IAAI,CAAC;IAC/D;GACF,CACF;EACF;EACA,KAAK,SAAS;GACZ,MAAM,OACJ,MAAM,YAAY,aAClB,MAAM,YAAY,aAClB,MAAM,YAAY,WACd,MAAM,UACN;GACN,MAAM,YAAY,SAAS,MAAM,QAAQ;GACzC,MAAM,cAAc,SAAS,MAAM,cAAc;GACjD,IAAI,QAAQ,YAAY,SAAS;IAC/B,MAAM,WAAW,CAAC,WAAW,WAAW,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,IAAI;IACnE,OAAO,CACL,gBACE,UAAU,UAAA,KAA0B,SAAS,QAAQ,OAAO,GAC5D,IACF,CACF;GACF;GACA,KACE,SACA,YACA,SACA,0DACF;GACA,OAAO,CACL,GAAG,mBACD,WACA,UACE,aACA,kBACA,SACA,eACA,OACF,GACA,IACF,CACF;EACF;EACA,KAAK,QACH,OAAO,YAAY,SAAS,SAAS,KAAK;EAC5C,KAAK,YACH,OAAO,gBAAgB,SAAS,SAAS,KAAK;EAChD,KAAK;EACL,KAAK,OACH,OAAO,gBAAgB,QAAQ,UAAU,SAAS,QAAQ,CAAC;EAC7D,KAAK,OACH,OACE,WAAW,SAAS,OAAO,KAC3B,gBAAgB,QAAQ,UAAU,SAAS,QAAQ,CAAC;EAExD,KAAK;EACL,KAAK,QACH,OAAO,CAAC;EACV,KAAK,SACH,OAAO,aAAa,SAAS,OAAO;EACtC,KAAK,YAAY;GACf,MAAM,QAAQ,SAAS,MAAM,QAAQ;GACrC,IACE,CAAC,QAAQ,qBACT,QAAQ,qBAAqB,MAAM,UAAA,MACnC;IACA,QAAQ,sBAAsB,MAAM;IACpC,OAAO,CAAC;KAAE,MAAM;KAAY,MAAM;IAAM,CAAC;GAC3C;GACA,QAAQ,oBAAoB;GAC5B,KACE,SACA,YACA,YACA,OAAO,qBAAqB,yCAC9B;GACA,OAAO,CACL;IACE,MAAM;IACN,MAAM;KACJ,MAAM;KACN,MAAM,UACJ,OACA,kBACA,YACA,iBACA,OACF;IACF;GACF,CACF;EACF;EACA,KAAK;GACH,KACE,SACA,WACA,SACA,8CACF;GACA,OAAO,CACL;IACE,MAAM;IACN,UAAU,CAAC;KAAE,MAAM;KAAU,MAAM;IAA0B,CAAC;GAChE,CACF;EACF,KAAK,YACH,OAAO,gBAAgB,SAAS,SAAS,KAAK;EAChD,KAAK,gBACH,OAAO,CAAC,gBAAgB,SAAS,SAAS,KAAK,CAAC;EAClD,KAAK,QACH,OAAO,CACL,GAAG,gBAAgB,QAAQ,UAAU,SAAS,QAAQ,CAAC,GACvD;GACE,MAAM;GACN,UAAU,CACR,cAAc,UAAU,WAAW,QAAQ,QAAQ,QAAQ,OAAO,CACpE;EACF,CACF;EACF;GACE,KACE,SACA,WACA,QAAQ,MACR,qCACF;GACA,OAAO,CAAC;CACZ;AACF;AAEA,SAAS,gBACP,MACA,SACA,OACc;CACd,IAAI,SAAS,KAAA,KAAa,SAAS,QAAQ,QAAA,IACzC,OAAO,CAAC;CAEV,MAAM,QAAQ,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,IAAI;CAChD,MAAM,SAAuB,CAAC;CAC9B,KAAK,IAAI,QAAQ,GAAG,QAAQ,MAAM,SAAS;EACzC,MAAM,UAAU,MAAM;EACtB,IAAI,YAAY,KAAA,KAAa,YAAY,MAAM;GAC7C,SAAS;GACT;EACF;EACA,IAAI,UAAU,OAAO,KAAK,QAAQ,SAAS,QAAQ;GACjD,MAAM,QAA+B,CAAC;GACtC,OAAO,QAAQ,MAAM,QAAQ;IAC3B,MAAM,YAAY,MAAM;IACxB,IAAI,CAAC,aAAa,CAAC,UAAU,SAAS,KAAK,UAAU,SAAS,QAC5D;IAEF,MAAM,KAAK,SAAS;IACpB,SAAS;GACX;GACA,OAAO,KAAK,GAAG,aAAa,OAAO,OAAO,CAAC;GAC3C;EACF;EACA,IAAI,UAAU,OAAO,KAAK,kBAAkB,IAAI,QAAQ,IAAI,GAAG;GAC7D,MAAM,WAAkC,CAAC;GACzC,OAAO,QAAQ,MAAM,QAAQ;IAC3B,MAAM,YAAY,MAAM;IACxB,IACE,CAAC,aACD,CAAC,UAAU,SAAS,KACpB,CAAC,kBAAkB,IAAI,UAAU,IAAI,GAErC;IAEF,SAAS,KAAK,SAAS;IACvB,SAAS;GACX;GACA,OAAO,KAAK,GAAG,eAAe,UAAU,OAAO,CAAC;GAChD;EACF;EACA,IAAI,OAAO,YAAY,YAAY,OAAO,YAAY,UACpD,OAAO,KAAK;GACV,MAAM;GACN,MAAM;IACJ,MAAM;IACN,MAAM,UACJ,OAAO,OAAO,GACd,kBACA,QACA,SACA,OACF;GACF;EACF,CAAC;OACI,IAAI,MAAM,QAAQ,OAAO,GAC9B,OAAO,KAAK,GAAG,gBAAgB,SAAS,SAAS,QAAQ,CAAC,CAAC;OAE3D,OAAO,KACL,GAAG,eAAe,SAAgC,SAAS,QAAQ,CAAC,CACtE;EAEF,SAAS;CACX;CACA,OAAO;AACT;AASA,SAAS,UACP,OACA,OACA,SACA,OACA,WACS;CACT,IAAI,MAAM,aAAa,GAAG;EACxB,IAAI,CAAC,MAAM,WAAW;GACpB,MAAM,YAAY;GAClB,QAAQ,QAAQ;EAClB;EACA,OAAO;CACT;CACA,MAAM,aAAa;CACnB,IAAI,QAAA,IAA6B;EAC/B,QAAQ,OAAO;EACf,OAAO;CACT;CACA,IAAI,MAAM,QAAQ,KAAK,GAAG;EACxB,IAAI,UAAU,IAAI,KAAK,GAAG;GACxB,QAAQ,OAAO;GACf,OAAO;EACT;EACA,UAAU,IAAI,KAAK;EACnB,MAAM,UAAU,MAAM,UAAU,MAAM,KACpC,OACA,GAAA,GAEF;EACA,IAAI,MAAM,SAAA,KAAuB,QAAQ,UAAU;EACnD,MAAM,SAAS,QAAQ,KAAK,SAC1B,UAAU,MAAM,QAAQ,GAAG,SAAS,OAAO,SAAS,CACtD;EACA,UAAU,OAAO,KAAK;EACtB,OAAO;CACT;CACA,IACE,UAAU,QACV,OAAO,UAAU,YACjB,cAAe,OACf;EACA,IAAI,UAAU,IAAI,KAAK,GAAG;GACxB,QAAQ,OAAO;GACf,OAAO;EACT;EACA,UAAU,IAAI,KAAK;EACnB,MAAM,SAAS;EACf,MAAM,SAAS;GACb,GAAG;GACH,UAAU,UACR,OAAO,aACP,QAAQ,GACR,SACA,OACA,SACF;EACF;EACA,UAAU,OAAO,KAAK;EACtB,OAAO;CACT;CACA,OAAO;AACT;;;;;;;;;;;;;;;AAgBA,SAAS,UACP,MACA,SACS;CACT,OAAO,UACL,MACA,GACA,SACA;EAAE,WAAW;EAAa,WAAW;CAAM,mBAC3C,IAAI,QAAQ,CACd;AACF;;AAGA,SAAgB,cACd,MACA,SACmB;CACnB,MAAM,UAAU,SAAS,YAAY,UAAU,UAAU;CACzD,MAAM,UAA6B;EACjC,UAAU,YAAY,UAAA,MAAA;EACtB;EACA,UAAU,CAAC;EACX,oBAAoB;EACpB,mBAAmB;EACnB,qBAAqB;CACvB;CACA,IAAI;EAYF,MAAM,EAAE,SAAS,cAXD,UAAU,OAAO,WAAW;GAC1C,MAAM,SACJ,WAAW,WACP,gCAAgC,YAAY,WAC5C,WAAW,UACT,yCACA,WAAW,UACT,8CACA;GACV,KAAK,SAAS,WAAW,QAAQ,MAAM;EACzC,CACqC,CAAU;EAC/C,MAAM,YAAY,gBAAgB,MAAM,SAAS,CAAC;EAClD,IAAI,UAAU,UAAU,QAAQ,UAC9B,OAAO;GAAE,QAAQ;GAAW,UAAU,QAAQ;EAAS;EAEzD,MAAM,OAAO,UAAU,MAAM,GAAG,QAAQ,WAAW,CAAC;EACpD,MAAM,UAAU,UAAU,SAAS,KAAK;EACxC,KACE,SACA,WACA,QACA,GAAG,QAAQ,GAAG,YAAY,IAAI,cAAc,cAAc,UAC5D;EACA,OAAO;GACL,QAAQ,CAAC,GAAG,MAAM,qBAAqB,OAAO,CAAC;GAC/C,UAAU,QAAQ;EACpB;CACF,QAAQ;EACN,OAAO;GACL,QAAQ,CAAC;GACT,UAAU,CACR;IACE,MAAM;IACN,WAAW;IACX,QAAQ;GACV,CACF;EACF;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"toSlackBlocks.js","names":[],"sources":["../../src/slack/toSlackBlocks.ts"],"sourcesContent":["import {\n normalizeSpec,\n type NormalizedUIElement,\n type NormalizedUINode,\n} from \"../ir\";\nimport {\n ACTION_ID_CAP,\n ACTIONS_ELEMENT_CAP,\n ALERT_TEXT_CAP,\n BUTTON_VALUE_CAP,\n CARD_ACTIONS_CAP,\n CARD_BODY_CAP,\n CARD_SUBTEXT_CAP,\n CARD_TITLE_CAP,\n CAROUSEL_CARD_CAP,\n CAROUSEL_CARD_MIN,\n CHILDREN_CAP,\n CONTEXT_ELEMENT_CAP,\n CONTEXT_TEXT_CAP,\n DATA_TABLE_CHAR_BUDGET,\n DATA_TABLE_COLUMN_CAP,\n DATA_TABLE_ROW_CAP,\n FACT_FIELD_CAP,\n FACT_FIELD_TEXT_CAP,\n HEADER_TEXT_CAP,\n INPUT_LABEL_CAP,\n INTERACTIVE_TEXT_CAP,\n MARKDOWN_TEXT_BUDGET,\n MAX_TRAVERSAL_DEPTH,\n MESSAGE_BLOCK_CAP,\n MODAL_BLOCK_CAP,\n NODE_BUDGET,\n PLACEHOLDER_TEXT_CAP,\n RADIO_OPTION_CAP,\n SECTION_TEXT_CAP,\n SELECT_OPTION_CAP,\n buildAlertBlock,\n buildAlertFallback,\n buildBlockBudgetNote,\n buildCardBlock,\n buildCarouselBlock,\n buildDataTableBlock,\n} from \"./constants\";\nimport type {\n SlackActionElement,\n SlackBlock,\n SlackButtonElement,\n SlackCardBlock,\n SlackConversionWarning,\n SlackDataTableCell,\n SlackImageBlock,\n SlackOption,\n SlackPlainText,\n SlackTextObject,\n SlackBlocksResult,\n ToSlackBlocksOptions,\n} from \"./types\";\n\ntype ConversionContext = {\n readonly blockCap: number;\n readonly surface: \"message\" | \"modal\";\n readonly warnings: SlackConversionWarning[];\n markdownCharacters: number;\n markdownExhausted: boolean;\n dataTableCharacters: number;\n};\n\nconst INTERACTIVE_TYPES = new Set([\n \"Button\",\n \"Select\",\n \"DatePicker\",\n \"Checkbox\",\n \"RadioGroup\",\n]);\n\nconst DATE_PATTERN = /^\\d{4}-\\d{2}-\\d{2}$/;\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst isElement = (node: NormalizedUINode): node is NormalizedUIElement =>\n isRecord(node);\n\nconst asString = (value: unknown): string =>\n typeof value === \"string\" ? value : \"\";\n\nconst warn = (\n context: ConversionContext,\n code: SlackConversionWarning[\"code\"],\n component: string,\n detail: string,\n) => {\n context.warnings.push({ code, component, detail });\n};\n\nconst clampText = (\n value: string,\n cap: number,\n component: string,\n field: string,\n context: ConversionContext,\n): string => {\n if (value.length <= cap) return value;\n warn(\n context,\n \"clamped\",\n component,\n `${field} was clamped to ${cap} characters.`,\n );\n return value.slice(0, cap);\n};\n\nconst asActionId = (\n action: unknown,\n component: string,\n context: ConversionContext,\n): string =>\n clampText(\n isRecord(action) && typeof action[\"type\"] === \"string\"\n ? action[\"type\"]\n : \"action\",\n ACTION_ID_CAP,\n component,\n \"action_id\",\n context,\n );\n\nconst plainText = (text: string): SlackPlainText => ({\n type: \"plain_text\",\n text,\n});\n\nconst actionValue = (action: unknown): string | undefined => {\n if (!isRecord(action)) return undefined;\n const { type: _type, ...payload } = action;\n if (Object.keys(payload).length === 0) return undefined;\n try {\n const serialized = JSON.stringify(payload);\n return serialized === \"{}\" ? undefined : serialized;\n } catch {\n return undefined;\n }\n};\n\nconst buttonElement = (\n label: string,\n buttonStyle: unknown,\n action: unknown,\n component: string,\n context: ConversionContext,\n): SlackButtonElement => {\n const serializedValue = actionValue(action);\n let value = serializedValue;\n if (value !== undefined && value.length > BUTTON_VALUE_CAP) {\n warn(\n context,\n \"dropped\",\n component,\n \"value was dropped because the action payload was too large to round-trip.\",\n );\n value = undefined;\n }\n const style =\n buttonStyle === \"primary\" || buttonStyle === \"danger\"\n ? buttonStyle\n : undefined;\n return {\n type: \"button\",\n text: plainText(\n clampText(label, INTERACTIVE_TEXT_CAP, component, \"label\", context),\n ),\n action_id: asActionId(action, component, context),\n ...(value !== undefined ? { value } : {}),\n ...(style !== undefined ? { style } : {}),\n };\n};\n\nconst optionFrom = (\n value: unknown,\n component: string,\n context: ConversionContext,\n): SlackOption | undefined => {\n if (\n !isRecord(value) ||\n typeof value[\"label\"] !== \"string\" ||\n typeof value[\"value\"] !== \"string\"\n ) {\n return undefined;\n }\n return {\n text: plainText(\n clampText(\n value[\"label\"],\n INTERACTIVE_TEXT_CAP,\n component,\n \"option label\",\n context,\n ),\n ),\n value: value[\"value\"],\n };\n};\n\nconst toActionElement = (\n element: NormalizedUIElement,\n context: ConversionContext,\n): SlackActionElement | undefined => {\n const { props, action } = element;\n switch (element.type) {\n case \"Button\":\n return buttonElement(\n asString(props[\"label\"]),\n props[\"buttonStyle\"],\n action,\n \"Button\",\n context,\n );\n case \"Select\": {\n const rawOptions = Array.isArray(props[\"options\"])\n ? props[\"options\"]\n : [];\n if (rawOptions.length > SELECT_OPTION_CAP) {\n warn(\n context,\n \"clamped\",\n \"Select\",\n `options were clamped to ${SELECT_OPTION_CAP} entries.`,\n );\n }\n const options = rawOptions\n .slice(0, SELECT_OPTION_CAP)\n .map((option) => optionFrom(option, \"Select\", context))\n .filter((option): option is SlackOption => option !== undefined);\n const placeholder = clampText(\n asString(props[\"placeholder\"]),\n PLACEHOLDER_TEXT_CAP,\n \"Select\",\n \"placeholder\",\n context,\n );\n return {\n type: \"static_select\",\n action_id: asActionId(action, \"Select\", context),\n options,\n ...(placeholder ? { placeholder: plainText(placeholder) } : {}),\n };\n }\n case \"DatePicker\": {\n const rawDate = asString(props[\"value\"]);\n const initialDate = DATE_PATTERN.test(rawDate) ? rawDate : undefined;\n if (rawDate && initialDate === undefined) {\n warn(\n context,\n \"dropped\",\n \"DatePicker\",\n \"value did not match the YYYY-MM-DD format and was dropped.\",\n );\n }\n return {\n type: \"datepicker\",\n action_id: asActionId(action, \"DatePicker\", context),\n ...(initialDate !== undefined ? { initial_date: initialDate } : {}),\n };\n }\n case \"Checkbox\": {\n const label = clampText(\n asString(props[\"label\"]),\n INTERACTIVE_TEXT_CAP,\n \"Checkbox\",\n \"label\",\n context,\n );\n const name = asString(props[\"name\"]);\n const option: SlackOption = {\n text: plainText(label),\n value: name || label,\n };\n return {\n type: \"checkboxes\",\n action_id: asActionId(action, \"Checkbox\", context),\n options: [option],\n ...(props[\"defaultChecked\"] === true\n ? { initial_options: [option] }\n : {}),\n };\n }\n case \"RadioGroup\": {\n const rawOptions = Array.isArray(props[\"options\"])\n ? props[\"options\"]\n : [];\n if (rawOptions.length > RADIO_OPTION_CAP) {\n warn(\n context,\n \"clamped\",\n \"RadioGroup\",\n `options were clamped to ${RADIO_OPTION_CAP} entries.`,\n );\n }\n const options = rawOptions\n .slice(0, RADIO_OPTION_CAP)\n .map((option) => optionFrom(option, \"RadioGroup\", context))\n .filter((option): option is SlackOption => option !== undefined);\n const selectedValue =\n typeof props[\"value\"] === \"string\"\n ? props[\"value\"]\n : asString(props[\"defaultValue\"]);\n const initialOption = options.find(\n (option) => option.value === selectedValue,\n );\n return {\n type: \"radio_buttons\",\n action_id: asActionId(action, \"RadioGroup\", context),\n options,\n ...(initialOption !== undefined\n ? { initial_option: initialOption }\n : {}),\n };\n }\n default:\n return undefined;\n }\n};\n\nconst normalizedList = (\n node: NormalizedUINode | undefined,\n): NormalizedUINode[] => {\n if (node === undefined || node === null) return [];\n if (!Array.isArray(node)) return [node];\n return node.flatMap((child) => normalizedList(child));\n};\n\nconst convertFacts = (\n facts: readonly NormalizedUIElement[],\n context: ConversionContext,\n): SlackBlock[] => {\n const fields = facts.map((fact) => {\n const label = asString(fact.props[\"label\"]);\n const value = asString(fact.props[\"value\"]);\n return {\n type: \"mrkdwn\" as const,\n text: clampText(\n `*${label}*\\n${value}`,\n FACT_FIELD_TEXT_CAP,\n \"Fact\",\n \"field\",\n context,\n ),\n };\n });\n const blocks: SlackBlock[] = [];\n for (let index = 0; index < fields.length; index += FACT_FIELD_CAP) {\n blocks.push({\n type: \"section\",\n fields: fields.slice(index, index + FACT_FIELD_CAP),\n });\n }\n return blocks;\n};\n\nconst convertActions = (\n elements: readonly NormalizedUIElement[],\n context: ConversionContext,\n): SlackBlock[] => {\n const converted = elements\n .map((element) => toActionElement(element, context))\n .filter((element): element is SlackActionElement => element !== undefined);\n const blocks: SlackBlock[] = [];\n for (let index = 0; index < converted.length; index += ACTIONS_ELEMENT_CAP) {\n blocks.push({\n type: \"actions\",\n elements: converted.slice(index, index + ACTIONS_ELEMENT_CAP),\n });\n }\n return blocks;\n};\n\nconst cardActionButtons = (\n props: Readonly<Record<string, unknown>>,\n context: ConversionContext,\n): SlackButtonElement[] => {\n const buttons: SlackButtonElement[] = [];\n for (const key of [\"confirm\", \"cancel\"] as const) {\n const footer = props[key];\n if (!isRecord(footer)) continue;\n buttons.push(\n buttonElement(\n asString(footer[\"label\"]),\n key === \"confirm\" ? \"primary\" : undefined,\n footer[\"$action\"],\n \"Card\",\n context,\n ),\n );\n }\n return buttons;\n};\n\nconst cardTitleText = (\n props: Readonly<Record<string, unknown>>,\n context: ConversionContext,\n): string | undefined => {\n const rawTitle = asString(props[\"title\"]);\n return rawTitle\n ? clampText(rawTitle, CARD_TITLE_CAP, \"Card\", \"title\", context)\n : undefined;\n};\n\nconst isCardNoOp = (node: NormalizedUINode): boolean =>\n isElement(node) && (node.type === \"Spacer\" || node.type === \"Icon\");\n\nconst collectText = (\n node: NormalizedUINode | undefined,\n depth: number,\n): string[] => {\n if (node === undefined || node === null || depth > MAX_TRAVERSAL_DEPTH) {\n return [];\n }\n if (typeof node === \"string\" || typeof node === \"number\") {\n return [String(node)];\n }\n if (Array.isArray(node)) {\n return node.flatMap((child) => collectText(child, depth + 1));\n }\n const element = node as NormalizedUIElement;\n const own = [\"text\", \"value\", \"label\", \"title\", \"description\"]\n .map((key) => element.props[key])\n .filter((value): value is string => typeof value === \"string\");\n return [...own, ...collectText(element.children, depth + 1)];\n};\n\ntype CardFields = {\n readonly heroImage: SlackImageBlock | undefined;\n readonly bodyRaw: string | undefined;\n readonly subtextRaw: string | undefined;\n readonly leftover: readonly NormalizedUINode[];\n};\n\n/**\n * Scans a card's children for the pieces the native card block shape can\n * carry (a hero image, body text, and subtext), each taken from the first\n * matching child. Anything left over means the card cannot map cleanly onto\n * the native shape.\n */\nconst extractCardFields = (element: NormalizedUIElement): CardFields => {\n const children = normalizedList(element.children);\n let heroImage: SlackImageBlock | undefined;\n let bodyRaw: string | undefined;\n let subtextRaw: string | undefined;\n const leftover: NormalizedUINode[] = [];\n for (const child of children) {\n if (heroImage === undefined && isElement(child) && child.type === \"Image\") {\n heroImage = {\n type: \"image\",\n image_url: asString(child.props[\"src\"]),\n alt_text: asString(child.props[\"alt\"]),\n };\n continue;\n }\n if (\n bodyRaw === undefined &&\n isElement(child) &&\n (child.type === \"Text\" || child.type === \"Markdown\")\n ) {\n bodyRaw = asString(child.props[\"value\"]);\n continue;\n }\n if (\n subtextRaw === undefined &&\n isElement(child) &&\n child.type === \"Caption\"\n ) {\n subtextRaw = asString(child.props[\"value\"]);\n continue;\n }\n if (isCardNoOp(child)) continue;\n leftover.push(child);\n }\n return { heroImage, bodyRaw, subtextRaw, leftover };\n};\n\n/** Assembles a native card block, clamping its body and subtext text. */\nconst assembleCleanCard = (\n titleText: string | undefined,\n fields: CardFields,\n buttons: readonly SlackButtonElement[],\n context: ConversionContext,\n): SlackCardBlock => {\n const body: SlackTextObject | undefined =\n fields.bodyRaw !== undefined\n ? {\n type: \"mrkdwn\",\n text: clampText(\n fields.bodyRaw,\n CARD_BODY_CAP,\n \"Card\",\n \"body\",\n context,\n ),\n }\n : undefined;\n const subtext: SlackTextObject | undefined =\n fields.subtextRaw !== undefined\n ? {\n type: \"mrkdwn\",\n text: clampText(\n fields.subtextRaw,\n CARD_SUBTEXT_CAP,\n \"Card\",\n \"subtext\",\n context,\n ),\n }\n : undefined;\n if (buttons.length > CARD_ACTIONS_CAP) {\n warn(\n context,\n \"clamped\",\n \"Card\",\n `actions were clamped to ${CARD_ACTIONS_CAP} entries.`,\n );\n }\n return buildCardBlock({\n ...(fields.heroImage !== undefined ? { heroImage: fields.heroImage } : {}),\n ...(titleText !== undefined\n ? { title: { type: \"mrkdwn\", text: titleText } }\n : {}),\n ...(body !== undefined ? { body } : {}),\n ...(subtext !== undefined ? { subtext } : {}),\n ...(buttons.length > 0\n ? { actions: buttons.slice(0, CARD_ACTIONS_CAP) }\n : {}),\n });\n};\n\n/**\n * A card requires at least one of `hero_image`, `title`, `body`, or `actions`\n * (see {@link SlackCardBlock}); a card with none of them is not a valid\n * native block and must be dropped instead of emitted.\n */\nconst isEmptyCard = (card: SlackCardBlock): boolean =>\n card.hero_image === undefined &&\n card.title === undefined &&\n card.body === undefined &&\n (card.actions === undefined || card.actions.length === 0);\n\nconst convertCard = (\n element: NormalizedUIElement,\n context: ConversionContext,\n depth: number,\n): SlackBlock[] => {\n const titleText = cardTitleText(element.props, context);\n const fields = extractCardFields(element);\n const buttons = cardActionButtons(element.props, context);\n\n if (fields.leftover.length > 0) {\n warn(\n context,\n \"fallback\",\n \"Card\",\n \"A card with content beyond hero_image, body, subtext, and actions was rendered inline.\",\n );\n return [\n ...(titleText\n ? [{ type: \"header\" as const, text: plainText(titleText) }]\n : []),\n ...convertSequence(element.children, context, depth + 1),\n ...(buttons.length > 0\n ? [{ type: \"actions\" as const, elements: buttons }]\n : []),\n ];\n }\n const card = assembleCleanCard(titleText, fields, buttons, context);\n if (isEmptyCard(card)) {\n warn(\n context,\n \"dropped\",\n \"Card\",\n \"A card with none of hero_image, title, body, or actions was dropped.\",\n );\n return [];\n }\n return [card];\n};\n\n/**\n * Degrades a card that cannot map cleanly into a title-and-body card block.\n * A carousel cannot fall back to a block sequence like a standalone card\n * can, so its content is clamped down to fit the card shape instead.\n */\nconst degradeCard = (\n element: NormalizedUIElement,\n context: ConversionContext,\n depth: number,\n): SlackCardBlock => {\n const rawTitle = asString(element.props[\"title\"]);\n const textChunks = collectText(element.children, depth + 1);\n const titleSource = rawTitle || textChunks[0] || \"\";\n const bodyChunks = rawTitle ? textChunks : textChunks.slice(1);\n const titleText = titleSource.slice(0, CARD_TITLE_CAP);\n const bodyText = bodyChunks.join(\"\\n\").slice(0, CARD_BODY_CAP);\n warn(\n context,\n \"clamped\",\n \"Card\",\n \"A card inside a carousel was degraded to title and body.\",\n );\n return buildCardBlock({\n ...(titleText ? { title: { type: \"mrkdwn\", text: titleText } } : {}),\n ...(bodyText ? { body: { type: \"mrkdwn\", text: bodyText } } : {}),\n });\n};\n\nconst convertCarouselCard = (\n element: NormalizedUIElement,\n context: ConversionContext,\n depth: number,\n): SlackCardBlock | undefined => {\n const fields = extractCardFields(element);\n const card =\n fields.leftover.length === 0\n ? assembleCleanCard(\n cardTitleText(element.props, context),\n fields,\n cardActionButtons(element.props, context),\n context,\n )\n : degradeCard(element, context, depth);\n if (isEmptyCard(card)) {\n warn(\n context,\n \"dropped\",\n \"Card\",\n \"A card with none of hero_image, title, body, or actions was dropped.\",\n );\n return undefined;\n }\n return card;\n};\n\nconst isBadgeOrCaption = (\n node: NormalizedUINode,\n): node is NormalizedUIElement =>\n isElement(node) && (node.type === \"Badge\" || node.type === \"Caption\");\n\nconst contextRow = (\n element: NormalizedUIElement,\n context: ConversionContext,\n): SlackBlock[] | undefined => {\n const allChildren = normalizedList(element.children);\n const children = allChildren.filter(isBadgeOrCaption);\n if (allChildren.length === 0 || children.length !== allChildren.length) {\n return undefined;\n }\n if (children.length > CONTEXT_ELEMENT_CAP) {\n warn(\n context,\n \"clamped\",\n \"Row\",\n `context elements were clamped to ${CONTEXT_ELEMENT_CAP} entries.`,\n );\n }\n return [\n {\n type: \"context\",\n elements: children.slice(0, CONTEXT_ELEMENT_CAP).map((child) => ({\n type: \"mrkdwn\",\n text: clampText(\n asString(child.props[\"value\"]),\n CONTEXT_TEXT_CAP,\n child.type,\n \"value\",\n context,\n ),\n })),\n },\n ];\n};\n\nconst convertListItem = (\n element: NormalizedUIElement,\n context: ConversionContext,\n depth: number,\n): SlackBlock => {\n const text = clampText(\n collectText(element.children, depth + 1).join(\"\\n\"),\n SECTION_TEXT_CAP,\n \"ListViewItem\",\n \"text\",\n context,\n );\n const accessory = isRecord(element.action)\n ? buttonElement(\"Open\", undefined, element.action, \"ListViewItem\", context)\n : undefined;\n return {\n type: \"section\",\n text: { type: \"mrkdwn\", text },\n ...(accessory !== undefined ? { accessory } : {}),\n };\n};\n\nconst convertListView = (\n element: NormalizedUIElement,\n context: ConversionContext,\n depth: number,\n): SlackBlock[] => {\n const items = normalizedList(element.children).filter(\n (child): child is NormalizedUIElement =>\n isElement(child) && child.type === \"ListViewItem\",\n );\n return items.flatMap((item, index) => [\n ...(index > 0 ? [{ type: \"divider\" as const }] : []),\n convertListItem(item, context, depth + 1),\n ]);\n};\n\nconst convertCarousel = (\n element: NormalizedUIElement,\n context: ConversionContext,\n depth: number,\n): SlackBlock[] => {\n const cardChildren: NormalizedUIElement[] = [];\n for (const child of normalizedList(element.children)) {\n if (isElement(child) && child.type === \"Card\") {\n cardChildren.push(child);\n } else {\n warn(context, \"dropped\", \"Carousel\", \"A non-card child was dropped.\");\n }\n }\n if (cardChildren.length > CAROUSEL_CARD_CAP) {\n warn(\n context,\n \"clamped\",\n \"Carousel\",\n `cards were clamped to ${CAROUSEL_CARD_CAP} entries.`,\n );\n }\n const cards = cardChildren\n .slice(0, CAROUSEL_CARD_CAP)\n .map((card) => convertCarouselCard(card, context, depth + 1))\n .filter((card): card is SlackCardBlock => card !== undefined);\n if (cards.length < CAROUSEL_CARD_MIN) {\n warn(\n context,\n \"dropped\",\n \"Carousel\",\n \"A carousel without renderable cards was dropped.\",\n );\n return [];\n }\n return [buildCarouselBlock(cards)];\n};\n\nconst toDataTableCell = (value: unknown): SlackDataTableCell | undefined => {\n if (typeof value === \"number\") {\n return { type: \"raw_number\", text: String(value) };\n }\n if (typeof value === \"string\" || typeof value === \"boolean\") {\n return { type: \"raw_text\", text: String(value) };\n }\n return undefined;\n};\n\nconst rowCharacters = (row: readonly SlackDataTableCell[]): number =>\n row.reduce((sum, cell) => sum + cell.text.length, 0);\n\nconst convertTable = (\n element: NormalizedUIElement,\n context: ConversionContext,\n): SlackBlock[] => {\n const rawColumns = Array.isArray(element.props[\"columns\"])\n ? element.props[\"columns\"]\n : [];\n const rawRows = Array.isArray(element.props[\"rows\"])\n ? element.props[\"rows\"]\n : [];\n if (rawColumns.length > DATA_TABLE_COLUMN_CAP) {\n warn(\n context,\n \"clamped\",\n \"Table\",\n `columns were clamped to ${DATA_TABLE_COLUMN_CAP} entries.`,\n );\n }\n if (rawRows.length > DATA_TABLE_ROW_CAP) {\n warn(\n context,\n \"clamped\",\n \"Table\",\n `rows were clamped to ${DATA_TABLE_ROW_CAP} entries.`,\n );\n }\n\n const columnHeaderRow: SlackDataTableCell[] = rawColumns\n .slice(0, DATA_TABLE_COLUMN_CAP)\n .filter(isRecord)\n .map((column) => ({\n type: \"raw_text\" as const,\n text: asString(column[\"label\"]),\n }));\n const dataRows: SlackDataTableCell[][] = rawRows\n .slice(0, DATA_TABLE_ROW_CAP)\n .map((row) =>\n (Array.isArray(row) ? row : [])\n .slice(0, DATA_TABLE_COLUMN_CAP)\n .map(\n (value) =>\n toDataTableCell(value) ?? { type: \"raw_text\" as const, text: \"\" },\n ),\n );\n const width = Math.max(\n columnHeaderRow.length,\n 0,\n ...dataRows.map((row) => row.length),\n );\n const padRow = (row: SlackDataTableCell[]): SlackDataTableCell[] =>\n row.length >= width\n ? row\n : [\n ...row,\n ...Array.from({ length: width - row.length }, () => ({\n type: \"raw_text\" as const,\n text: \"\",\n })),\n ];\n const headerRow = padRow(columnHeaderRow);\n\n const headerCharacters = rowCharacters(headerRow);\n if (context.dataTableCharacters + headerCharacters > DATA_TABLE_CHAR_BUDGET) {\n warn(\n context,\n \"dropped\",\n \"Table\",\n `the table was dropped because its header row exceeds the ${DATA_TABLE_CHAR_BUDGET}-character table budget.`,\n );\n return [];\n }\n\n const kept: SlackDataTableCell[][] = [headerRow];\n let tableCharacters = headerCharacters;\n let budgetClamped = false;\n for (const row of dataRows.map(padRow)) {\n const chars = rowCharacters(row);\n if (\n context.dataTableCharacters + tableCharacters + chars >\n DATA_TABLE_CHAR_BUDGET\n ) {\n budgetClamped = true;\n break;\n }\n kept.push(row);\n tableCharacters += chars;\n }\n context.dataTableCharacters += tableCharacters;\n if (budgetClamped) {\n warn(\n context,\n \"clamped\",\n \"Table\",\n `rows were clamped to fit the ${DATA_TABLE_CHAR_BUDGET}-character table budget.`,\n );\n }\n\n return [buildDataTableBlock(kept)];\n};\n\nconst convertElement = (\n element: NormalizedUIElement,\n context: ConversionContext,\n depth: number,\n): SlackBlock[] => {\n const { props } = element;\n switch (element.type) {\n case \"Header\":\n return [\n {\n type: \"header\",\n text: plainText(\n clampText(\n asString(props[\"text\"]),\n HEADER_TEXT_CAP,\n \"Header\",\n \"text\",\n context,\n ),\n ),\n },\n ];\n case \"Text\":\n return [\n {\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: clampText(\n asString(props[\"value\"]),\n SECTION_TEXT_CAP,\n \"Text\",\n \"value\",\n context,\n ),\n },\n },\n ];\n case \"Caption\":\n case \"Badge\":\n return [\n {\n type: \"context\",\n elements: [\n {\n type: \"mrkdwn\",\n text: clampText(\n asString(props[\"value\"]),\n CONTEXT_TEXT_CAP,\n element.type,\n \"value\",\n context,\n ),\n },\n ],\n },\n ];\n case \"Fact\":\n return convertFacts([element], context);\n case \"Image\":\n return [\n {\n type: \"image\",\n image_url: asString(props[\"src\"]),\n alt_text: asString(props[\"alt\"]),\n },\n ];\n case \"Divider\":\n return [{ type: \"divider\" }];\n case \"Button\":\n case \"Select\":\n case \"DatePicker\":\n case \"Checkbox\":\n case \"RadioGroup\":\n return convertActions([element], context);\n case \"Input\": {\n const label = clampText(\n asString(props[\"label\"]),\n INPUT_LABEL_CAP,\n \"Input\",\n \"label\",\n context,\n );\n const placeholder = clampText(\n asString(props[\"placeholder\"]),\n PLACEHOLDER_TEXT_CAP,\n \"Input\",\n \"placeholder\",\n context,\n );\n return [\n {\n type: \"input\",\n label: plainText(label),\n element: {\n type: \"plain_text_input\",\n action_id: asActionId(element.action, \"Input\", context),\n ...(props[\"multiline\"] === true ? { multiline: true } : {}),\n ...(placeholder ? { placeholder: plainText(placeholder) } : {}),\n },\n },\n ];\n }\n case \"Alert\": {\n const tone =\n props[\"tone\"] === \"success\" ||\n props[\"tone\"] === \"warning\" ||\n props[\"tone\"] === \"danger\"\n ? props[\"tone\"]\n : \"info\";\n const titleText = asString(props[\"title\"]);\n const description = asString(props[\"description\"]);\n if (context.surface === \"modal\") {\n const combined = [titleText, description].filter(Boolean).join(\"\\n\");\n return [\n buildAlertBlock(\n clampText(combined, ALERT_TEXT_CAP, \"Alert\", \"text\", context),\n tone,\n ),\n ];\n }\n warn(\n context,\n \"fallback\",\n \"Alert\",\n \"Alert was rendered with message-surface fallback blocks.\",\n );\n return [\n ...buildAlertFallback(\n titleText,\n clampText(\n description,\n SECTION_TEXT_CAP,\n \"Alert\",\n \"description\",\n context,\n ),\n tone,\n ),\n ];\n }\n case \"Card\":\n return convertCard(element, context, depth);\n case \"Carousel\":\n return convertCarousel(element, context, depth);\n case \"Col\":\n case \"Box\":\n return convertSequence(element.children, context, depth + 1);\n case \"Row\":\n return (\n contextRow(element, context) ??\n convertSequence(element.children, context, depth + 1)\n );\n case \"Spacer\":\n case \"Icon\":\n return [];\n case \"Table\":\n return convertTable(element, context);\n case \"Markdown\": {\n const value = asString(props[\"value\"]);\n if (\n !context.markdownExhausted &&\n context.markdownCharacters + value.length <= MARKDOWN_TEXT_BUDGET\n ) {\n context.markdownCharacters += value.length;\n return [{ type: \"markdown\", text: value }];\n }\n context.markdownExhausted = true;\n warn(\n context,\n \"fallback\",\n \"Markdown\",\n `The ${MARKDOWN_TEXT_BUDGET}-character markdown budget was exceeded.`,\n );\n return [\n {\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: clampText(\n value,\n SECTION_TEXT_CAP,\n \"Markdown\",\n \"fallback text\",\n context,\n ),\n },\n },\n ];\n }\n case \"Chart\":\n warn(\n context,\n \"dropped\",\n \"Chart\",\n \"Chart was replaced by a Slack omission note.\",\n );\n return [\n {\n type: \"context\",\n elements: [{ type: \"mrkdwn\", text: \"Chart omitted on Slack.\" }],\n },\n ];\n case \"ListView\":\n return convertListView(element, context, depth);\n case \"ListViewItem\":\n return [convertListItem(element, context, depth)];\n case \"Form\":\n return [\n ...convertSequence(element.children, context, depth + 1),\n {\n type: \"actions\",\n elements: [\n buttonElement(\"Submit\", \"primary\", element.action, \"Form\", context),\n ],\n },\n ];\n default:\n warn(\n context,\n \"dropped\",\n element.type,\n \"Unknown component type was dropped.\",\n );\n return [];\n }\n};\n\nfunction convertSequence(\n node: NormalizedUINode | undefined,\n context: ConversionContext,\n depth: number,\n): SlackBlock[] {\n if (node === undefined || node === null || depth > MAX_TRAVERSAL_DEPTH) {\n return [];\n }\n const nodes = Array.isArray(node) ? node : [node];\n const blocks: SlackBlock[] = [];\n for (let index = 0; index < nodes.length;) {\n const current = nodes[index];\n if (current === undefined || current === null) {\n index += 1;\n continue;\n }\n if (isElement(current) && current.type === \"Fact\") {\n const facts: NormalizedUIElement[] = [];\n while (index < nodes.length) {\n const candidate = nodes[index];\n if (!candidate || !isElement(candidate) || candidate.type !== \"Fact\") {\n break;\n }\n facts.push(candidate);\n index += 1;\n }\n blocks.push(...convertFacts(facts, context));\n continue;\n }\n if (isElement(current) && INTERACTIVE_TYPES.has(current.type)) {\n const controls: NormalizedUIElement[] = [];\n while (index < nodes.length) {\n const candidate = nodes[index];\n if (\n !candidate ||\n !isElement(candidate) ||\n !INTERACTIVE_TYPES.has(candidate.type)\n ) {\n break;\n }\n controls.push(candidate);\n index += 1;\n }\n blocks.push(...convertActions(controls, context));\n continue;\n }\n if (typeof current === \"string\" || typeof current === \"number\") {\n blocks.push({\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: clampText(\n String(current),\n SECTION_TEXT_CAP,\n \"Text\",\n \"value\",\n context,\n ),\n },\n });\n } else if (Array.isArray(current)) {\n blocks.push(...convertSequence(current, context, depth + 1));\n } else {\n blocks.push(\n ...convertElement(current as NormalizedUIElement, context, depth + 1),\n );\n }\n index += 1;\n }\n return blocks;\n}\n\ninterface BoundState {\n remaining: number;\n exhausted: boolean;\n}\n\ntype ClampReason = \"children\" | \"budget\" | \"cycle\" | \"depth\";\n\nfunction boundNode(\n value: unknown,\n depth: number,\n onClamp: (reason: ClampReason) => void,\n state: BoundState,\n ancestors: WeakSet<object>,\n): unknown {\n if (state.remaining <= 0) {\n if (!state.exhausted) {\n state.exhausted = true;\n onClamp(\"budget\");\n }\n return null;\n }\n state.remaining -= 1;\n if (depth > MAX_TRAVERSAL_DEPTH) {\n onClamp(\"depth\");\n return null;\n }\n if (Array.isArray(value)) {\n if (ancestors.has(value)) {\n onClamp(\"cycle\");\n return null;\n }\n ancestors.add(value);\n const bounded = Array.prototype.slice.call(\n value,\n 0,\n CHILDREN_CAP,\n ) as unknown[];\n if (value.length > CHILDREN_CAP) onClamp(\"children\");\n const result = bounded.map((item) =>\n boundNode(item, depth + 1, onClamp, state, ancestors),\n );\n ancestors.delete(value);\n return result;\n }\n if (\n value !== null &&\n typeof value === \"object\" &&\n \"children\" in (value as Record<string, unknown>)\n ) {\n if (ancestors.has(value)) {\n onClamp(\"cycle\");\n return null;\n }\n ancestors.add(value);\n const record = value as Record<string, unknown>;\n const result = {\n ...record,\n children: boundNode(\n record[\"children\"],\n depth + 1,\n onClamp,\n state,\n ancestors,\n ),\n };\n ancestors.delete(value);\n return result;\n }\n return value;\n}\n\n/**\n * Produces a bounded plain copy of a raw generative-ui spec before it\n * reaches `normalizeSpec`, whose own traversal of the root array or any\n * `children` array walks the full reported length of a hostile proxied\n * array before any per-field cap downstream ever applies. Every array\n * (root, or `children` at any depth) is capped to {@link CHILDREN_CAP}\n * entries via `Array.prototype.slice`, which bounds even a proxy with a\n * fabricated `length`; recursion itself is capped at\n * {@link MAX_TRAVERSAL_DEPTH}. `onClamp` fires once per level that was\n * truncated, receiving the reason for that truncation: `\"children\"`,\n * `\"depth\"`, `\"budget\"`, or `\"cycle\"`. The walk also spends a total budget of\n * {@link NODE_BUDGET} nodes, so shared references cannot multiply work\n * exponentially, and a node that is its own ancestor is cut to `null`.\n */\nfunction boundSpec(\n spec: unknown,\n onClamp: (reason: ClampReason) => void,\n): unknown {\n return boundNode(\n spec,\n 0,\n onClamp,\n { remaining: NODE_BUDGET, exhausted: false },\n new WeakSet(),\n );\n}\n\n/** Converts a generative-UI tree into Slack Block Kit JSON and downgrade warnings. */\nexport function toSlackBlocks(\n node: unknown,\n options?: ToSlackBlocksOptions,\n): SlackBlocksResult {\n const surface = options?.surface === \"modal\" ? \"modal\" : \"message\";\n const context: ConversionContext = {\n blockCap: surface === \"modal\" ? MODAL_BLOCK_CAP : MESSAGE_BLOCK_CAP,\n surface,\n warnings: [],\n markdownCharacters: 0,\n markdownExhausted: false,\n dataTableCharacters: 0,\n };\n try {\n const bounded = boundSpec(node, (reason) => {\n const detail =\n reason === \"budget\"\n ? `the tree was truncated after ${NODE_BUDGET} nodes.`\n : reason === \"cycle\"\n ? \"a self-referencing node was dropped.\"\n : reason === \"depth\"\n ? `nodes deeper than ${MAX_TRAVERSAL_DEPTH} levels were dropped.`\n : `children were clamped to ${CHILDREN_CAP} entries.`;\n warn(context, \"clamped\", \"Root\", detail);\n });\n const { root } = normalizeSpec(bounded as never);\n const converted = convertSequence(root, context, 0);\n if (converted.length <= context.blockCap) {\n return { blocks: converted, warnings: context.warnings };\n }\n const kept = converted.slice(0, context.blockCap - 1);\n const omitted = converted.length - kept.length;\n warn(\n context,\n \"clamped\",\n \"Root\",\n `${omitted} ${omitted === 1 ? \"block was\" : \"blocks were\"} omitted.`,\n );\n return {\n blocks: [...kept, buildBlockBudgetNote(omitted)],\n warnings: context.warnings,\n };\n } catch {\n return {\n blocks: [],\n warnings: [\n {\n code: \"dropped\",\n component: \"Root\",\n detail: \"Input could not be converted.\",\n },\n ],\n };\n }\n}\n"],"mappings":"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"}
|
|
@@ -454,7 +454,8 @@ function convertSequence(node, context, depth) {
|
|
|
454
454
|
/** Builds a full card (schema envelope plus body) from an already-normalized root, checking the soft payload budget. */
|
|
455
455
|
function convertRootToCard(root, context) {
|
|
456
456
|
context.usedInputIds = /* @__PURE__ */ new Set();
|
|
457
|
-
const
|
|
457
|
+
const body = convertSequence(root, context, 0);
|
|
458
|
+
const card = buildCard(body);
|
|
458
459
|
const size = utf8ByteLength(JSON.stringify(card));
|
|
459
460
|
if (size > 8e4) warn(context, "clamped", "Root", `the card is ${size} bytes, exceeding Teams' 100 KB bot message limit.`);
|
|
460
461
|
return card;
|
|
@@ -474,7 +475,8 @@ function toAdaptiveCard(node, _options) {
|
|
|
474
475
|
usedInputIds: /* @__PURE__ */ new Set()
|
|
475
476
|
};
|
|
476
477
|
try {
|
|
477
|
-
const
|
|
478
|
+
const bounded = boundSpec(node, (reason) => warn(context, "clamped", "Root", clampReasonDetail(reason)));
|
|
479
|
+
const { root } = normalizeSpec(bounded);
|
|
478
480
|
return {
|
|
479
481
|
card: convertRootToCard(root, context),
|
|
480
482
|
warnings: context.warnings
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toAdaptiveCard.js","names":[],"sources":["../../src/teams/toAdaptiveCard.ts"],"sourcesContent":["import {\n normalizeSpec,\n type NormalizedUIElement,\n type NormalizedUINode,\n} from \"../ir\";\nimport { boundSpec } from \"./boundSpec\";\nimport {\n CHOICE_OPTION_CAP,\n MAX_TRAVERSAL_DEPTH,\n PAYLOAD_SOFT_CAP,\n PRIMARY_ACTION_CAP,\n TABLE_COLUMN_CAP,\n TABLE_ROW_CAP,\n buildCard,\n buildSubmitAction,\n clampReasonDetail,\n utf8ByteLength,\n} from \"./constants\";\nimport type {\n AdaptiveCardResult,\n TeamsActionSet,\n TeamsAdaptiveCard,\n TeamsCardElement,\n TeamsColumn,\n TeamsContainer,\n TeamsContainerStyle,\n TeamsConversionWarning,\n TeamsFact,\n TeamsInputChoice,\n TeamsTable,\n TeamsTableColumnDefinition,\n TeamsTableRow,\n TeamsTextBlock,\n TeamsTextSize,\n ToAdaptiveCardOptions,\n} from \"./types\";\n\n/** Shared mutable conversion state threaded through one top-level conversion. */\nexport interface ConversionContext {\n readonly warnings: TeamsConversionWarning[];\n usedInputIds: Set<string>;\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst isElement = (node: NormalizedUINode): node is NormalizedUIElement =>\n isRecord(node);\n\nconst asString = (value: unknown): string =>\n typeof value === \"string\" ? value : \"\";\n\nconst DATE_PATTERN = /^\\d{4}-\\d{2}-\\d{2}$/;\n\nconst SUBTLE_COLORS = new Set([\n \"secondary\",\n \"alpha-70\",\n \"white\",\n \"white-70\",\n \"white-50\",\n]);\nconst isSubtleColor = (color: unknown): boolean =>\n typeof color === \"string\" && SUBTLE_COLORS.has(color);\n\nconst BOLDER_WEIGHTS = new Set([\"medium\", \"semibold\", \"bold\"]);\nconst isBolderWeight = (weight: unknown): boolean =>\n typeof weight === \"string\" && BOLDER_WEIGHTS.has(weight);\n\nconst TEXT_SIZE_MAP: Record<string, TeamsTextSize> = {\n sm: \"small\",\n lg: \"medium\",\n xl: \"large\",\n \"2xl\": \"extraLarge\",\n \"3xl\": \"extraLarge\",\n};\n\nconst warn = (\n context: ConversionContext,\n code: TeamsConversionWarning[\"code\"],\n component: string,\n detail: string,\n) => {\n context.warnings.push({ code, component, detail });\n};\n\n/**\n * The `aui` key is reserved for the submit envelope (see\n * {@link buildSubmitData} and `decodeSubmitData`): Adaptive Cards merge\n * every input's current value into the same submit object keyed by its\n * `id`, so an input id of `aui` would clobber the envelope. Renamed to an\n * unused id derived from `aui_` instead of being emitted as-is.\n */\nconst RESERVED_INPUT_ID = \"aui\";\n\nfunction reservedSafeId(\n id: string,\n component: string,\n context: ConversionContext,\n): string {\n const reserved = id === RESERVED_INPUT_ID;\n const base = reserved ? `${RESERVED_INPUT_ID}_` : id;\n let candidate = base;\n let n = 2;\n while (context.usedInputIds.has(candidate)) {\n candidate = `${base}_${n}`;\n n += 1;\n }\n if (reserved) {\n warn(\n context,\n \"clamped\",\n component,\n `the input id \"${RESERVED_INPUT_ID}\" collides with the submit envelope's reserved key and was renamed to \"${candidate}\".`,\n );\n } else if (candidate !== base) {\n warn(\n context,\n \"clamped\",\n component,\n `the input id \"${base}\" was already used on this card and was renamed to \"${candidate}\".`,\n );\n }\n context.usedInputIds.add(candidate);\n return candidate;\n}\n\n/**\n * The shared bounded-iteration primitive: slices `value` to `cap` entries\n * without ever reading past that many indices, so a hostile array (sparse or\n * proxied with a fabricated `length`) cannot stall the event loop.\n */\nconst clampArray = (\n value: unknown,\n cap: number,\n): { readonly items: unknown[]; readonly truncated: boolean } => {\n if (!Array.isArray(value)) return { items: [], truncated: false };\n return { items: value.slice(0, cap), truncated: value.length > cap };\n};\n\nconst normalizedList = (\n node: NormalizedUINode | undefined,\n): NormalizedUINode[] => {\n if (node === undefined || node === null) return [];\n if (!Array.isArray(node)) return [node];\n return node.flatMap((child) => normalizedList(child));\n};\n\nfunction textBlock(\n text: string,\n extra?: Partial<Omit<TeamsTextBlock, \"type\" | \"text\" | \"wrap\">>,\n): TeamsTextBlock {\n return { type: \"TextBlock\", text, wrap: true, ...extra };\n}\n\nfunction applyPending(\n element: TeamsCardElement,\n separator: boolean,\n spacing: boolean,\n): TeamsCardElement {\n if (!separator && !spacing) return element;\n return {\n ...element,\n ...(separator ? { separator: true as const } : {}),\n ...(spacing ? { spacing: \"large\" as const } : {}),\n };\n}\n\nconst toChoice = (option: unknown): TeamsInputChoice | undefined => {\n if (!isRecord(option) || typeof option[\"value\"] !== \"string\") {\n return undefined;\n }\n return { title: asString(option[\"label\"]), value: option[\"value\"] };\n};\n\nconst choicesFrom = (\n optionsProp: unknown,\n component: string,\n context: ConversionContext,\n): TeamsInputChoice[] => {\n const { items, truncated } = clampArray(optionsProp, CHOICE_OPTION_CAP);\n if (truncated) {\n warn(\n context,\n \"clamped\",\n component,\n `options were clamped to ${CHOICE_OPTION_CAP} entries.`,\n );\n }\n const choices: TeamsInputChoice[] = [];\n for (const item of items) {\n const choice = toChoice(item);\n if (choice !== undefined) choices.push(choice);\n }\n return choices;\n};\n\nfunction convertFacts(facts: readonly NormalizedUIElement[]): TeamsCardElement {\n const set: TeamsFact[] = facts.map((fact) => ({\n title: asString(fact.props[\"label\"]),\n value: asString(fact.props[\"value\"]),\n }));\n return { type: \"FactSet\", facts: set };\n}\n\n// Action.Submit has no visual style concept, so a Button's `buttonStyle`\n// (positive/destructive or otherwise) has nothing to map onto and is dropped.\nfunction convertButtons(\n buttons: readonly NormalizedUIElement[],\n context: ConversionContext,\n): TeamsActionSet {\n if (buttons.length > PRIMARY_ACTION_CAP) {\n warn(\n context,\n \"clamped\",\n \"Button\",\n `actions beyond ${PRIMARY_ACTION_CAP} were set to secondary mode.`,\n );\n }\n const actions = buttons.map((button, index) =>\n buildSubmitAction(\n asString(button.props[\"label\"]),\n button.action,\n index >= PRIMARY_ACTION_CAP ? \"secondary\" : undefined,\n ),\n );\n return { type: \"ActionSet\", actions };\n}\n\nfunction withCompanionSubmit(\n element: NormalizedUIElement,\n input: TeamsCardElement,\n context: ConversionContext,\n): TeamsCardElement[] {\n if (element.action === undefined) return [input];\n warn(\n context,\n \"fallback\",\n element.type,\n \"Teams inputs cannot dispatch on change; a companion submit action was appended.\",\n );\n return [\n input,\n {\n type: \"ActionSet\",\n actions: [buildSubmitAction(\"Submit\", element.action)],\n },\n ];\n}\n\nfunction cardFooterActionSet(\n props: Readonly<Record<string, unknown>>,\n): TeamsActionSet | undefined {\n const actions = ([\"confirm\", \"cancel\"] as const)\n .map((key) => props[key])\n .filter(isRecord)\n .map((footer) =>\n buildSubmitAction(asString(footer[\"label\"]), footer[\"$action\"]),\n );\n return actions.length > 0 ? { type: \"ActionSet\", actions } : undefined;\n}\n\nconst ALERT_STYLE_MAP: Record<string, TeamsContainerStyle> = {\n info: \"accent\",\n success: \"good\",\n warning: \"warning\",\n danger: \"attention\",\n};\n\nconst stringifyCell = (value: unknown): string => {\n if (typeof value === \"string\") return value;\n if (typeof value === \"number\" || typeof value === \"boolean\")\n return String(value);\n return \"\";\n};\n\nfunction convertTable(\n props: Readonly<Record<string, unknown>>,\n context: ConversionContext,\n): TeamsTable {\n const { items: rawColumns, truncated: columnsTruncated } = clampArray(\n props[\"columns\"],\n TABLE_COLUMN_CAP,\n );\n const { items: rawRows, truncated: rowsTruncated } = clampArray(\n props[\"rows\"],\n TABLE_ROW_CAP,\n );\n if (columnsTruncated) {\n warn(\n context,\n \"clamped\",\n \"Table\",\n `columns were clamped to ${TABLE_COLUMN_CAP} entries.`,\n );\n }\n if (rowsTruncated) {\n warn(\n context,\n \"clamped\",\n \"Table\",\n `rows were clamped to ${TABLE_ROW_CAP} entries.`,\n );\n }\n\n const hasColumns = rawColumns.length > 0;\n const columns: TeamsTableColumnDefinition[] = rawColumns.map(() => ({\n width: 1 as const,\n }));\n const headerRow: TeamsTableRow | undefined = hasColumns\n ? {\n type: \"TableRow\",\n cells: rawColumns.map((column) => ({\n type: \"TableCell\" as const,\n items: [textBlock(isRecord(column) ? asString(column[\"label\"]) : \"\")],\n })),\n }\n : undefined;\n const dataRows: TeamsTableRow[] = rawRows.map((row) => ({\n type: \"TableRow\",\n cells: (Array.isArray(row) ? row.slice(0, TABLE_COLUMN_CAP) : []).map(\n (cell) => ({\n type: \"TableCell\" as const,\n items: [textBlock(stringifyCell(cell))],\n }),\n ),\n }));\n\n return {\n type: \"Table\",\n firstRowAsHeaders: hasColumns,\n columns,\n rows: headerRow !== undefined ? [headerRow, ...dataRows] : dataRows,\n };\n}\n\nfunction convertListViewItem(\n element: NormalizedUIElement,\n context: ConversionContext,\n depth: number,\n): TeamsContainer {\n const items = convertSequence(element.children, context, depth + 1);\n const selectAction = isRecord(element.action)\n ? buildSubmitAction(\"Open\", element.action)\n : undefined;\n return {\n type: \"Container\",\n items,\n ...(selectAction !== undefined ? { selectAction } : {}),\n };\n}\n\nexport function convertElement(\n element: NormalizedUIElement,\n context: ConversionContext,\n depth: number,\n): TeamsCardElement[] {\n const { props } = element;\n switch (element.type) {\n case \"Header\":\n return [\n textBlock(asString(props[\"text\"]), {\n size: \"large\",\n weight: \"bolder\",\n style: \"heading\",\n }),\n ];\n case \"Text\": {\n const mappedSize =\n typeof props[\"size\"] === \"string\"\n ? TEXT_SIZE_MAP[props[\"size\"]]\n : undefined;\n return [\n textBlock(asString(props[\"value\"]), {\n ...(mappedSize !== undefined ? { size: mappedSize } : {}),\n ...(isBolderWeight(props[\"weight\"]) ? { weight: \"bolder\" } : {}),\n ...(isSubtleColor(props[\"color\"]) ? { isSubtle: true } : {}),\n }),\n ];\n }\n case \"Caption\":\n return [\n textBlock(asString(props[\"value\"]), { size: \"small\", isSubtle: true }),\n ];\n case \"Badge\":\n return [\n textBlock(asString(props[\"value\"]), { size: \"small\", isSubtle: true }),\n ];\n case \"Markdown\":\n // Teams renders a strict subset of markdown (no headings, tables, images,\n // preformatted blocks, or blockquotes); unsupported syntax renders literally.\n return [textBlock(asString(props[\"value\"]))];\n case \"Fact\":\n return [convertFacts([element])];\n case \"Image\": {\n const size = props[\"size\"];\n let mappedSize: \"small\" | \"medium\" | \"large\" | undefined;\n if (size === \"sm\") mappedSize = \"small\";\n else if (size === \"md\") mappedSize = \"medium\";\n else if (size === \"lg\") mappedSize = \"large\";\n else if (typeof size === \"number\") {\n warn(context, \"dropped\", \"Image\", \"A numeric size was dropped.\");\n }\n return [\n {\n type: \"Image\",\n url: asString(props[\"src\"]),\n altText: asString(props[\"alt\"]),\n ...(mappedSize !== undefined ? { size: mappedSize } : {}),\n },\n ];\n }\n case \"Divider\":\n case \"Spacer\":\n case \"Icon\":\n return [];\n case \"Button\":\n return [convertButtons([element], context)];\n case \"Select\": {\n const name = asString(props[\"name\"]);\n const placeholder = asString(props[\"placeholder\"]);\n const label = asString(props[\"label\"]);\n const input: TeamsCardElement = {\n type: \"Input.ChoiceSet\",\n id: reservedSafeId(name || \"select\", \"Select\", context),\n style: \"compact\",\n choices: choicesFrom(props[\"options\"], \"Select\", context),\n ...(placeholder ? { placeholder } : {}),\n ...(label ? { label } : {}),\n };\n return withCompanionSubmit(element, input, context);\n }\n case \"RadioGroup\": {\n const name = asString(props[\"name\"]);\n const label = asString(props[\"label\"]);\n const defaultValue = props[\"defaultValue\"];\n const input: TeamsCardElement = {\n type: \"Input.ChoiceSet\",\n id: reservedSafeId(name || \"radiogroup\", \"RadioGroup\", context),\n style: \"expanded\",\n choices: choicesFrom(props[\"options\"], \"RadioGroup\", context),\n ...(typeof defaultValue === \"string\" && defaultValue\n ? { value: defaultValue }\n : {}),\n ...(label ? { label } : {}),\n };\n return withCompanionSubmit(element, input, context);\n }\n case \"Checkbox\": {\n const name = asString(props[\"name\"]);\n const label = asString(props[\"label\"]);\n const input: TeamsCardElement = {\n type: \"Input.Toggle\",\n id: reservedSafeId(name || label || \"checkbox\", \"Checkbox\", context),\n title: label,\n value: props[\"defaultChecked\"] === true ? \"true\" : \"false\",\n valueOn: \"true\",\n valueOff: \"false\",\n };\n return withCompanionSubmit(element, input, context);\n }\n case \"Input\": {\n const name = asString(props[\"name\"]);\n const label = asString(props[\"label\"]);\n const placeholder = asString(props[\"placeholder\"]);\n const input: TeamsCardElement = {\n type: \"Input.Text\",\n id: reservedSafeId(name || \"input\", \"Input\", context),\n ...(label ? { label } : {}),\n ...(placeholder ? { placeholder } : {}),\n ...(props[\"multiline\"] === true ? { isMultiline: true } : {}),\n };\n return withCompanionSubmit(element, input, context);\n }\n case \"DatePicker\": {\n const name = asString(props[\"name\"]);\n const label = asString(props[\"label\"]);\n const rawValue = props[\"value\"];\n const value =\n typeof rawValue === \"string\" && DATE_PATTERN.test(rawValue)\n ? rawValue\n : undefined;\n const rawMin = props[\"min\"];\n const min =\n typeof rawMin === \"string\" && DATE_PATTERN.test(rawMin)\n ? rawMin\n : undefined;\n const rawMax = props[\"max\"];\n const max =\n typeof rawMax === \"string\" && DATE_PATTERN.test(rawMax)\n ? rawMax\n : undefined;\n const input: TeamsCardElement = {\n type: \"Input.Date\",\n id: reservedSafeId(name || \"datepicker\", \"DatePicker\", context),\n ...(label ? { label } : {}),\n ...(value !== undefined ? { value } : {}),\n ...(min !== undefined ? { min } : {}),\n ...(max !== undefined ? { max } : {}),\n };\n return withCompanionSubmit(element, input, context);\n }\n case \"Form\":\n return [\n ...convertSequence(element.children, context, depth + 1),\n {\n type: \"ActionSet\",\n actions: [buildSubmitAction(\"Submit\", element.action)],\n },\n ];\n case \"Card\": {\n const titleText = asString(props[\"title\"]);\n const items: TeamsCardElement[] = [];\n if (titleText) {\n items.push(\n textBlock(titleText, {\n size: \"large\",\n weight: \"bolder\",\n style: \"heading\",\n }),\n );\n }\n items.push(...convertSequence(element.children, context, depth + 1));\n const container: TeamsContainer = {\n type: \"Container\",\n style: \"default\",\n items,\n };\n const actionSet = cardFooterActionSet(props);\n return actionSet !== undefined ? [container, actionSet] : [container];\n }\n case \"Col\":\n case \"Box\":\n return [\n {\n type: \"Container\",\n items: convertSequence(element.children, context, depth + 1),\n },\n ];\n case \"Row\": {\n const children = normalizedList(element.children);\n if (children.length > 3) {\n warn(context, \"clamped\", \"Row\", \"Teams recommends at most 3 columns.\");\n }\n const columns: TeamsColumn[] = children.map((child) => ({\n type: \"Column\",\n width: \"auto\",\n items: convertSequence(child, context, depth + 1),\n }));\n return [{ type: \"ColumnSet\", columns }];\n }\n case \"ListView\": {\n const containers: TeamsContainer[] = [];\n for (const child of normalizedList(element.children)) {\n if (!isElement(child)) continue;\n if (child.type === \"ListViewItem\") {\n containers.push(convertListViewItem(child, context, depth + 1));\n } else {\n convertElement(child, context, depth + 1);\n }\n }\n return containers.map((container, index) =>\n index > 0 ? { ...container, separator: true } : container,\n );\n }\n case \"ListViewItem\":\n return [convertListViewItem(element, context, depth)];\n case \"Table\":\n return [convertTable(props, context)];\n case \"Alert\": {\n const tone = props[\"tone\"];\n const style =\n typeof tone === \"string\"\n ? (ALERT_STYLE_MAP[tone] ?? \"default\")\n : \"default\";\n const titleText = asString(props[\"title\"]);\n const description = asString(props[\"description\"]);\n const items: TeamsCardElement[] = [];\n if (titleText) items.push(textBlock(titleText, { weight: \"bolder\" }));\n if (description) items.push(textBlock(description));\n return [{ type: \"Container\", style, items }];\n }\n case \"Carousel\": {\n warn(\n context,\n \"fallback\",\n \"Carousel\",\n \"A carousel was rendered as sequential cards because it is not at the root.\",\n );\n const cards = normalizedList(element.children).filter(\n (child): child is NormalizedUIElement =>\n isElement(child) && child.type === \"Card\",\n );\n return cards.flatMap((card) => convertElement(card, context, depth + 1));\n }\n case \"Chart\":\n warn(\n context,\n \"dropped\",\n \"Chart\",\n \"Chart was replaced by a Teams omission note.\",\n );\n return [\n textBlock(\"Chart omitted on Teams.\", { isSubtle: true, size: \"small\" }),\n ];\n default:\n warn(\n context,\n \"dropped\",\n element.type,\n \"Unknown component type was dropped.\",\n );\n return [];\n }\n}\n\n/**\n * Walks a normalized sequence into card body elements. Nested arrays are\n * flattened into one flat sibling sequence first (via {@link normalizedList}),\n * so a `Divider`/`Fact`/`Button` run merges correctly even when the model\n * wrapped some of its siblings in their own array. Consecutive `Fact`\n * siblings merge into one FactSet and consecutive `Button` siblings merge\n * into one ActionSet; a `Divider`/`Spacer` emits nothing itself but sets\n * `separator`/`spacing` on the next emitted sibling (a trailing or orphan\n * one is dropped silently, since there is no sibling left to carry it).\n * `depth` advances by exactly one per element-nesting level: this call does\n * not itself add a level, since flattening a sibling array or converting a\n * sibling element is not a descent; only a container's own recursive\n * `convertSequence(children, depth + 1)` call is.\n */\nexport function convertSequence(\n node: NormalizedUINode | undefined,\n context: ConversionContext,\n depth: number,\n): TeamsCardElement[] {\n if (node === undefined || node === null || depth > MAX_TRAVERSAL_DEPTH) {\n return [];\n }\n const nodes = normalizedList(node);\n const items: TeamsCardElement[] = [];\n let pendingSeparator = false;\n let pendingSpacing = false;\n\n const emit = (converted: TeamsCardElement[]) => {\n const [first, ...rest] = converted;\n if (first === undefined) return;\n items.push(applyPending(first, pendingSeparator, pendingSpacing), ...rest);\n pendingSeparator = false;\n pendingSpacing = false;\n };\n\n for (let index = 0; index < nodes.length;) {\n const current = nodes[index];\n if (current === undefined || current === null) {\n index += 1;\n continue;\n }\n if (isElement(current) && current.type === \"Divider\") {\n pendingSeparator = true;\n index += 1;\n continue;\n }\n if (isElement(current) && current.type === \"Spacer\") {\n pendingSpacing = true;\n index += 1;\n continue;\n }\n if (isElement(current) && current.type === \"Fact\") {\n const facts: NormalizedUIElement[] = [];\n while (index < nodes.length) {\n const candidate = nodes[index];\n if (!candidate || !isElement(candidate) || candidate.type !== \"Fact\") {\n break;\n }\n facts.push(candidate);\n index += 1;\n }\n emit([convertFacts(facts)]);\n continue;\n }\n if (isElement(current) && current.type === \"Button\") {\n const buttons: NormalizedUIElement[] = [];\n while (index < nodes.length) {\n const candidate = nodes[index];\n if (\n !candidate ||\n !isElement(candidate) ||\n candidate.type !== \"Button\"\n ) {\n break;\n }\n buttons.push(candidate);\n index += 1;\n }\n emit([convertButtons(buttons, context)]);\n continue;\n }\n if (typeof current === \"string\" || typeof current === \"number\") {\n emit([textBlock(String(current))]);\n } else {\n emit(convertElement(current as NormalizedUIElement, context, depth));\n }\n index += 1;\n }\n return items;\n}\n\n/** Builds a full card (schema envelope plus body) from an already-normalized root, checking the soft payload budget. */\nexport function convertRootToCard(\n root: NormalizedUINode | readonly NormalizedUINode[],\n context: ConversionContext,\n): TeamsAdaptiveCard {\n context.usedInputIds = new Set();\n const body = convertSequence(root, context, 0);\n const card = buildCard(body);\n const size = utf8ByteLength(JSON.stringify(card));\n if (size > PAYLOAD_SOFT_CAP) {\n warn(\n context,\n \"clamped\",\n \"Root\",\n `the card is ${size} bytes, exceeding Teams' 100 KB bot message limit.`,\n );\n }\n return card;\n}\n\n/**\n * Converts a generative-ui tree into a Microsoft Teams Adaptive Card and\n * non-fatal downgrade warnings. Sizes, weights, and colors map to Adaptive\n * Card's semantic enums rather than raw values. An Input/Select/RadioGroup/\n * Checkbox/DatePicker whose id would be the reserved {@link RESERVED_INPUT_ID}\n * is renamed with a warning (see `decodeSubmitData`). Never throws: an\n * unknown `$type` is skipped with a \"dropped\" warning, and a malformed input\n * resolves to an empty card plus a \"dropped\" warning instead of throwing.\n */\nexport function toAdaptiveCard(\n node: unknown,\n _options?: ToAdaptiveCardOptions,\n): AdaptiveCardResult {\n const context: ConversionContext = { warnings: [], usedInputIds: new Set() };\n try {\n const bounded = boundSpec(node, (reason) =>\n warn(context, \"clamped\", \"Root\", clampReasonDetail(reason)),\n );\n const { root } = normalizeSpec(bounded as never);\n return {\n card: convertRootToCard(root, context),\n warnings: context.warnings,\n };\n } catch {\n return {\n card: buildCard([]),\n warnings: [\n {\n code: \"dropped\",\n component: \"Root\",\n detail: \"Input could not be converted.\",\n },\n ],\n };\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"toAdaptiveCard.js","names":[],"sources":["../../src/teams/toAdaptiveCard.ts"],"sourcesContent":["import {\n normalizeSpec,\n type NormalizedUIElement,\n type NormalizedUINode,\n} from \"../ir\";\nimport { boundSpec } from \"./boundSpec\";\nimport {\n CHOICE_OPTION_CAP,\n MAX_TRAVERSAL_DEPTH,\n PAYLOAD_SOFT_CAP,\n PRIMARY_ACTION_CAP,\n TABLE_COLUMN_CAP,\n TABLE_ROW_CAP,\n buildCard,\n buildSubmitAction,\n clampReasonDetail,\n utf8ByteLength,\n} from \"./constants\";\nimport type {\n AdaptiveCardResult,\n TeamsActionSet,\n TeamsAdaptiveCard,\n TeamsCardElement,\n TeamsColumn,\n TeamsContainer,\n TeamsContainerStyle,\n TeamsConversionWarning,\n TeamsFact,\n TeamsInputChoice,\n TeamsTable,\n TeamsTableColumnDefinition,\n TeamsTableRow,\n TeamsTextBlock,\n TeamsTextSize,\n ToAdaptiveCardOptions,\n} from \"./types\";\n\n/** Shared mutable conversion state threaded through one top-level conversion. */\nexport interface ConversionContext {\n readonly warnings: TeamsConversionWarning[];\n usedInputIds: Set<string>;\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst isElement = (node: NormalizedUINode): node is NormalizedUIElement =>\n isRecord(node);\n\nconst asString = (value: unknown): string =>\n typeof value === \"string\" ? value : \"\";\n\nconst DATE_PATTERN = /^\\d{4}-\\d{2}-\\d{2}$/;\n\nconst SUBTLE_COLORS = new Set([\n \"secondary\",\n \"alpha-70\",\n \"white\",\n \"white-70\",\n \"white-50\",\n]);\nconst isSubtleColor = (color: unknown): boolean =>\n typeof color === \"string\" && SUBTLE_COLORS.has(color);\n\nconst BOLDER_WEIGHTS = new Set([\"medium\", \"semibold\", \"bold\"]);\nconst isBolderWeight = (weight: unknown): boolean =>\n typeof weight === \"string\" && BOLDER_WEIGHTS.has(weight);\n\nconst TEXT_SIZE_MAP: Record<string, TeamsTextSize> = {\n sm: \"small\",\n lg: \"medium\",\n xl: \"large\",\n \"2xl\": \"extraLarge\",\n \"3xl\": \"extraLarge\",\n};\n\nconst warn = (\n context: ConversionContext,\n code: TeamsConversionWarning[\"code\"],\n component: string,\n detail: string,\n) => {\n context.warnings.push({ code, component, detail });\n};\n\n/**\n * The `aui` key is reserved for the submit envelope (see\n * {@link buildSubmitData} and `decodeSubmitData`): Adaptive Cards merge\n * every input's current value into the same submit object keyed by its\n * `id`, so an input id of `aui` would clobber the envelope. Renamed to an\n * unused id derived from `aui_` instead of being emitted as-is.\n */\nconst RESERVED_INPUT_ID = \"aui\";\n\nfunction reservedSafeId(\n id: string,\n component: string,\n context: ConversionContext,\n): string {\n const reserved = id === RESERVED_INPUT_ID;\n const base = reserved ? `${RESERVED_INPUT_ID}_` : id;\n let candidate = base;\n let n = 2;\n while (context.usedInputIds.has(candidate)) {\n candidate = `${base}_${n}`;\n n += 1;\n }\n if (reserved) {\n warn(\n context,\n \"clamped\",\n component,\n `the input id \"${RESERVED_INPUT_ID}\" collides with the submit envelope's reserved key and was renamed to \"${candidate}\".`,\n );\n } else if (candidate !== base) {\n warn(\n context,\n \"clamped\",\n component,\n `the input id \"${base}\" was already used on this card and was renamed to \"${candidate}\".`,\n );\n }\n context.usedInputIds.add(candidate);\n return candidate;\n}\n\n/**\n * The shared bounded-iteration primitive: slices `value` to `cap` entries\n * without ever reading past that many indices, so a hostile array (sparse or\n * proxied with a fabricated `length`) cannot stall the event loop.\n */\nconst clampArray = (\n value: unknown,\n cap: number,\n): { readonly items: unknown[]; readonly truncated: boolean } => {\n if (!Array.isArray(value)) return { items: [], truncated: false };\n return { items: value.slice(0, cap), truncated: value.length > cap };\n};\n\nconst normalizedList = (\n node: NormalizedUINode | undefined,\n): NormalizedUINode[] => {\n if (node === undefined || node === null) return [];\n if (!Array.isArray(node)) return [node];\n return node.flatMap((child) => normalizedList(child));\n};\n\nfunction textBlock(\n text: string,\n extra?: Partial<Omit<TeamsTextBlock, \"type\" | \"text\" | \"wrap\">>,\n): TeamsTextBlock {\n return { type: \"TextBlock\", text, wrap: true, ...extra };\n}\n\nfunction applyPending(\n element: TeamsCardElement,\n separator: boolean,\n spacing: boolean,\n): TeamsCardElement {\n if (!separator && !spacing) return element;\n return {\n ...element,\n ...(separator ? { separator: true as const } : {}),\n ...(spacing ? { spacing: \"large\" as const } : {}),\n };\n}\n\nconst toChoice = (option: unknown): TeamsInputChoice | undefined => {\n if (!isRecord(option) || typeof option[\"value\"] !== \"string\") {\n return undefined;\n }\n return { title: asString(option[\"label\"]), value: option[\"value\"] };\n};\n\nconst choicesFrom = (\n optionsProp: unknown,\n component: string,\n context: ConversionContext,\n): TeamsInputChoice[] => {\n const { items, truncated } = clampArray(optionsProp, CHOICE_OPTION_CAP);\n if (truncated) {\n warn(\n context,\n \"clamped\",\n component,\n `options were clamped to ${CHOICE_OPTION_CAP} entries.`,\n );\n }\n const choices: TeamsInputChoice[] = [];\n for (const item of items) {\n const choice = toChoice(item);\n if (choice !== undefined) choices.push(choice);\n }\n return choices;\n};\n\nfunction convertFacts(facts: readonly NormalizedUIElement[]): TeamsCardElement {\n const set: TeamsFact[] = facts.map((fact) => ({\n title: asString(fact.props[\"label\"]),\n value: asString(fact.props[\"value\"]),\n }));\n return { type: \"FactSet\", facts: set };\n}\n\n// Action.Submit has no visual style concept, so a Button's `buttonStyle`\n// (positive/destructive or otherwise) has nothing to map onto and is dropped.\nfunction convertButtons(\n buttons: readonly NormalizedUIElement[],\n context: ConversionContext,\n): TeamsActionSet {\n if (buttons.length > PRIMARY_ACTION_CAP) {\n warn(\n context,\n \"clamped\",\n \"Button\",\n `actions beyond ${PRIMARY_ACTION_CAP} were set to secondary mode.`,\n );\n }\n const actions = buttons.map((button, index) =>\n buildSubmitAction(\n asString(button.props[\"label\"]),\n button.action,\n index >= PRIMARY_ACTION_CAP ? \"secondary\" : undefined,\n ),\n );\n return { type: \"ActionSet\", actions };\n}\n\nfunction withCompanionSubmit(\n element: NormalizedUIElement,\n input: TeamsCardElement,\n context: ConversionContext,\n): TeamsCardElement[] {\n if (element.action === undefined) return [input];\n warn(\n context,\n \"fallback\",\n element.type,\n \"Teams inputs cannot dispatch on change; a companion submit action was appended.\",\n );\n return [\n input,\n {\n type: \"ActionSet\",\n actions: [buildSubmitAction(\"Submit\", element.action)],\n },\n ];\n}\n\nfunction cardFooterActionSet(\n props: Readonly<Record<string, unknown>>,\n): TeamsActionSet | undefined {\n const actions = ([\"confirm\", \"cancel\"] as const)\n .map((key) => props[key])\n .filter(isRecord)\n .map((footer) =>\n buildSubmitAction(asString(footer[\"label\"]), footer[\"$action\"]),\n );\n return actions.length > 0 ? { type: \"ActionSet\", actions } : undefined;\n}\n\nconst ALERT_STYLE_MAP: Record<string, TeamsContainerStyle> = {\n info: \"accent\",\n success: \"good\",\n warning: \"warning\",\n danger: \"attention\",\n};\n\nconst stringifyCell = (value: unknown): string => {\n if (typeof value === \"string\") return value;\n if (typeof value === \"number\" || typeof value === \"boolean\")\n return String(value);\n return \"\";\n};\n\nfunction convertTable(\n props: Readonly<Record<string, unknown>>,\n context: ConversionContext,\n): TeamsTable {\n const { items: rawColumns, truncated: columnsTruncated } = clampArray(\n props[\"columns\"],\n TABLE_COLUMN_CAP,\n );\n const { items: rawRows, truncated: rowsTruncated } = clampArray(\n props[\"rows\"],\n TABLE_ROW_CAP,\n );\n if (columnsTruncated) {\n warn(\n context,\n \"clamped\",\n \"Table\",\n `columns were clamped to ${TABLE_COLUMN_CAP} entries.`,\n );\n }\n if (rowsTruncated) {\n warn(\n context,\n \"clamped\",\n \"Table\",\n `rows were clamped to ${TABLE_ROW_CAP} entries.`,\n );\n }\n\n const hasColumns = rawColumns.length > 0;\n const columns: TeamsTableColumnDefinition[] = rawColumns.map(() => ({\n width: 1 as const,\n }));\n const headerRow: TeamsTableRow | undefined = hasColumns\n ? {\n type: \"TableRow\",\n cells: rawColumns.map((column) => ({\n type: \"TableCell\" as const,\n items: [textBlock(isRecord(column) ? asString(column[\"label\"]) : \"\")],\n })),\n }\n : undefined;\n const dataRows: TeamsTableRow[] = rawRows.map((row) => ({\n type: \"TableRow\",\n cells: (Array.isArray(row) ? row.slice(0, TABLE_COLUMN_CAP) : []).map(\n (cell) => ({\n type: \"TableCell\" as const,\n items: [textBlock(stringifyCell(cell))],\n }),\n ),\n }));\n\n return {\n type: \"Table\",\n firstRowAsHeaders: hasColumns,\n columns,\n rows: headerRow !== undefined ? [headerRow, ...dataRows] : dataRows,\n };\n}\n\nfunction convertListViewItem(\n element: NormalizedUIElement,\n context: ConversionContext,\n depth: number,\n): TeamsContainer {\n const items = convertSequence(element.children, context, depth + 1);\n const selectAction = isRecord(element.action)\n ? buildSubmitAction(\"Open\", element.action)\n : undefined;\n return {\n type: \"Container\",\n items,\n ...(selectAction !== undefined ? { selectAction } : {}),\n };\n}\n\nexport function convertElement(\n element: NormalizedUIElement,\n context: ConversionContext,\n depth: number,\n): TeamsCardElement[] {\n const { props } = element;\n switch (element.type) {\n case \"Header\":\n return [\n textBlock(asString(props[\"text\"]), {\n size: \"large\",\n weight: \"bolder\",\n style: \"heading\",\n }),\n ];\n case \"Text\": {\n const mappedSize =\n typeof props[\"size\"] === \"string\"\n ? TEXT_SIZE_MAP[props[\"size\"]]\n : undefined;\n return [\n textBlock(asString(props[\"value\"]), {\n ...(mappedSize !== undefined ? { size: mappedSize } : {}),\n ...(isBolderWeight(props[\"weight\"]) ? { weight: \"bolder\" } : {}),\n ...(isSubtleColor(props[\"color\"]) ? { isSubtle: true } : {}),\n }),\n ];\n }\n case \"Caption\":\n return [\n textBlock(asString(props[\"value\"]), { size: \"small\", isSubtle: true }),\n ];\n case \"Badge\":\n return [\n textBlock(asString(props[\"value\"]), { size: \"small\", isSubtle: true }),\n ];\n case \"Markdown\":\n // Teams renders a strict subset of markdown (no headings, tables, images,\n // preformatted blocks, or blockquotes); unsupported syntax renders literally.\n return [textBlock(asString(props[\"value\"]))];\n case \"Fact\":\n return [convertFacts([element])];\n case \"Image\": {\n const size = props[\"size\"];\n let mappedSize: \"small\" | \"medium\" | \"large\" | undefined;\n if (size === \"sm\") mappedSize = \"small\";\n else if (size === \"md\") mappedSize = \"medium\";\n else if (size === \"lg\") mappedSize = \"large\";\n else if (typeof size === \"number\") {\n warn(context, \"dropped\", \"Image\", \"A numeric size was dropped.\");\n }\n return [\n {\n type: \"Image\",\n url: asString(props[\"src\"]),\n altText: asString(props[\"alt\"]),\n ...(mappedSize !== undefined ? { size: mappedSize } : {}),\n },\n ];\n }\n case \"Divider\":\n case \"Spacer\":\n case \"Icon\":\n return [];\n case \"Button\":\n return [convertButtons([element], context)];\n case \"Select\": {\n const name = asString(props[\"name\"]);\n const placeholder = asString(props[\"placeholder\"]);\n const label = asString(props[\"label\"]);\n const input: TeamsCardElement = {\n type: \"Input.ChoiceSet\",\n id: reservedSafeId(name || \"select\", \"Select\", context),\n style: \"compact\",\n choices: choicesFrom(props[\"options\"], \"Select\", context),\n ...(placeholder ? { placeholder } : {}),\n ...(label ? { label } : {}),\n };\n return withCompanionSubmit(element, input, context);\n }\n case \"RadioGroup\": {\n const name = asString(props[\"name\"]);\n const label = asString(props[\"label\"]);\n const defaultValue = props[\"defaultValue\"];\n const input: TeamsCardElement = {\n type: \"Input.ChoiceSet\",\n id: reservedSafeId(name || \"radiogroup\", \"RadioGroup\", context),\n style: \"expanded\",\n choices: choicesFrom(props[\"options\"], \"RadioGroup\", context),\n ...(typeof defaultValue === \"string\" && defaultValue\n ? { value: defaultValue }\n : {}),\n ...(label ? { label } : {}),\n };\n return withCompanionSubmit(element, input, context);\n }\n case \"Checkbox\": {\n const name = asString(props[\"name\"]);\n const label = asString(props[\"label\"]);\n const input: TeamsCardElement = {\n type: \"Input.Toggle\",\n id: reservedSafeId(name || label || \"checkbox\", \"Checkbox\", context),\n title: label,\n value: props[\"defaultChecked\"] === true ? \"true\" : \"false\",\n valueOn: \"true\",\n valueOff: \"false\",\n };\n return withCompanionSubmit(element, input, context);\n }\n case \"Input\": {\n const name = asString(props[\"name\"]);\n const label = asString(props[\"label\"]);\n const placeholder = asString(props[\"placeholder\"]);\n const input: TeamsCardElement = {\n type: \"Input.Text\",\n id: reservedSafeId(name || \"input\", \"Input\", context),\n ...(label ? { label } : {}),\n ...(placeholder ? { placeholder } : {}),\n ...(props[\"multiline\"] === true ? { isMultiline: true } : {}),\n };\n return withCompanionSubmit(element, input, context);\n }\n case \"DatePicker\": {\n const name = asString(props[\"name\"]);\n const label = asString(props[\"label\"]);\n const rawValue = props[\"value\"];\n const value =\n typeof rawValue === \"string\" && DATE_PATTERN.test(rawValue)\n ? rawValue\n : undefined;\n const rawMin = props[\"min\"];\n const min =\n typeof rawMin === \"string\" && DATE_PATTERN.test(rawMin)\n ? rawMin\n : undefined;\n const rawMax = props[\"max\"];\n const max =\n typeof rawMax === \"string\" && DATE_PATTERN.test(rawMax)\n ? rawMax\n : undefined;\n const input: TeamsCardElement = {\n type: \"Input.Date\",\n id: reservedSafeId(name || \"datepicker\", \"DatePicker\", context),\n ...(label ? { label } : {}),\n ...(value !== undefined ? { value } : {}),\n ...(min !== undefined ? { min } : {}),\n ...(max !== undefined ? { max } : {}),\n };\n return withCompanionSubmit(element, input, context);\n }\n case \"Form\":\n return [\n ...convertSequence(element.children, context, depth + 1),\n {\n type: \"ActionSet\",\n actions: [buildSubmitAction(\"Submit\", element.action)],\n },\n ];\n case \"Card\": {\n const titleText = asString(props[\"title\"]);\n const items: TeamsCardElement[] = [];\n if (titleText) {\n items.push(\n textBlock(titleText, {\n size: \"large\",\n weight: \"bolder\",\n style: \"heading\",\n }),\n );\n }\n items.push(...convertSequence(element.children, context, depth + 1));\n const container: TeamsContainer = {\n type: \"Container\",\n style: \"default\",\n items,\n };\n const actionSet = cardFooterActionSet(props);\n return actionSet !== undefined ? [container, actionSet] : [container];\n }\n case \"Col\":\n case \"Box\":\n return [\n {\n type: \"Container\",\n items: convertSequence(element.children, context, depth + 1),\n },\n ];\n case \"Row\": {\n const children = normalizedList(element.children);\n if (children.length > 3) {\n warn(context, \"clamped\", \"Row\", \"Teams recommends at most 3 columns.\");\n }\n const columns: TeamsColumn[] = children.map((child) => ({\n type: \"Column\",\n width: \"auto\",\n items: convertSequence(child, context, depth + 1),\n }));\n return [{ type: \"ColumnSet\", columns }];\n }\n case \"ListView\": {\n const containers: TeamsContainer[] = [];\n for (const child of normalizedList(element.children)) {\n if (!isElement(child)) continue;\n if (child.type === \"ListViewItem\") {\n containers.push(convertListViewItem(child, context, depth + 1));\n } else {\n convertElement(child, context, depth + 1);\n }\n }\n return containers.map((container, index) =>\n index > 0 ? { ...container, separator: true } : container,\n );\n }\n case \"ListViewItem\":\n return [convertListViewItem(element, context, depth)];\n case \"Table\":\n return [convertTable(props, context)];\n case \"Alert\": {\n const tone = props[\"tone\"];\n const style =\n typeof tone === \"string\"\n ? (ALERT_STYLE_MAP[tone] ?? \"default\")\n : \"default\";\n const titleText = asString(props[\"title\"]);\n const description = asString(props[\"description\"]);\n const items: TeamsCardElement[] = [];\n if (titleText) items.push(textBlock(titleText, { weight: \"bolder\" }));\n if (description) items.push(textBlock(description));\n return [{ type: \"Container\", style, items }];\n }\n case \"Carousel\": {\n warn(\n context,\n \"fallback\",\n \"Carousel\",\n \"A carousel was rendered as sequential cards because it is not at the root.\",\n );\n const cards = normalizedList(element.children).filter(\n (child): child is NormalizedUIElement =>\n isElement(child) && child.type === \"Card\",\n );\n return cards.flatMap((card) => convertElement(card, context, depth + 1));\n }\n case \"Chart\":\n warn(\n context,\n \"dropped\",\n \"Chart\",\n \"Chart was replaced by a Teams omission note.\",\n );\n return [\n textBlock(\"Chart omitted on Teams.\", { isSubtle: true, size: \"small\" }),\n ];\n default:\n warn(\n context,\n \"dropped\",\n element.type,\n \"Unknown component type was dropped.\",\n );\n return [];\n }\n}\n\n/**\n * Walks a normalized sequence into card body elements. Nested arrays are\n * flattened into one flat sibling sequence first (via {@link normalizedList}),\n * so a `Divider`/`Fact`/`Button` run merges correctly even when the model\n * wrapped some of its siblings in their own array. Consecutive `Fact`\n * siblings merge into one FactSet and consecutive `Button` siblings merge\n * into one ActionSet; a `Divider`/`Spacer` emits nothing itself but sets\n * `separator`/`spacing` on the next emitted sibling (a trailing or orphan\n * one is dropped silently, since there is no sibling left to carry it).\n * `depth` advances by exactly one per element-nesting level: this call does\n * not itself add a level, since flattening a sibling array or converting a\n * sibling element is not a descent; only a container's own recursive\n * `convertSequence(children, depth + 1)` call is.\n */\nexport function convertSequence(\n node: NormalizedUINode | undefined,\n context: ConversionContext,\n depth: number,\n): TeamsCardElement[] {\n if (node === undefined || node === null || depth > MAX_TRAVERSAL_DEPTH) {\n return [];\n }\n const nodes = normalizedList(node);\n const items: TeamsCardElement[] = [];\n let pendingSeparator = false;\n let pendingSpacing = false;\n\n const emit = (converted: TeamsCardElement[]) => {\n const [first, ...rest] = converted;\n if (first === undefined) return;\n items.push(applyPending(first, pendingSeparator, pendingSpacing), ...rest);\n pendingSeparator = false;\n pendingSpacing = false;\n };\n\n for (let index = 0; index < nodes.length;) {\n const current = nodes[index];\n if (current === undefined || current === null) {\n index += 1;\n continue;\n }\n if (isElement(current) && current.type === \"Divider\") {\n pendingSeparator = true;\n index += 1;\n continue;\n }\n if (isElement(current) && current.type === \"Spacer\") {\n pendingSpacing = true;\n index += 1;\n continue;\n }\n if (isElement(current) && current.type === \"Fact\") {\n const facts: NormalizedUIElement[] = [];\n while (index < nodes.length) {\n const candidate = nodes[index];\n if (!candidate || !isElement(candidate) || candidate.type !== \"Fact\") {\n break;\n }\n facts.push(candidate);\n index += 1;\n }\n emit([convertFacts(facts)]);\n continue;\n }\n if (isElement(current) && current.type === \"Button\") {\n const buttons: NormalizedUIElement[] = [];\n while (index < nodes.length) {\n const candidate = nodes[index];\n if (\n !candidate ||\n !isElement(candidate) ||\n candidate.type !== \"Button\"\n ) {\n break;\n }\n buttons.push(candidate);\n index += 1;\n }\n emit([convertButtons(buttons, context)]);\n continue;\n }\n if (typeof current === \"string\" || typeof current === \"number\") {\n emit([textBlock(String(current))]);\n } else {\n emit(convertElement(current as NormalizedUIElement, context, depth));\n }\n index += 1;\n }\n return items;\n}\n\n/** Builds a full card (schema envelope plus body) from an already-normalized root, checking the soft payload budget. */\nexport function convertRootToCard(\n root: NormalizedUINode | readonly NormalizedUINode[],\n context: ConversionContext,\n): TeamsAdaptiveCard {\n context.usedInputIds = new Set();\n const body = convertSequence(root, context, 0);\n const card = buildCard(body);\n const size = utf8ByteLength(JSON.stringify(card));\n if (size > PAYLOAD_SOFT_CAP) {\n warn(\n context,\n \"clamped\",\n \"Root\",\n `the card is ${size} bytes, exceeding Teams' 100 KB bot message limit.`,\n );\n }\n return card;\n}\n\n/**\n * Converts a generative-ui tree into a Microsoft Teams Adaptive Card and\n * non-fatal downgrade warnings. Sizes, weights, and colors map to Adaptive\n * Card's semantic enums rather than raw values. An Input/Select/RadioGroup/\n * Checkbox/DatePicker whose id would be the reserved {@link RESERVED_INPUT_ID}\n * is renamed with a warning (see `decodeSubmitData`). Never throws: an\n * unknown `$type` is skipped with a \"dropped\" warning, and a malformed input\n * resolves to an empty card plus a \"dropped\" warning instead of throwing.\n */\nexport function toAdaptiveCard(\n node: unknown,\n _options?: ToAdaptiveCardOptions,\n): AdaptiveCardResult {\n const context: ConversionContext = { warnings: [], usedInputIds: new Set() };\n try {\n const bounded = boundSpec(node, (reason) =>\n warn(context, \"clamped\", \"Root\", clampReasonDetail(reason)),\n );\n const { root } = normalizeSpec(bounded as never);\n return {\n card: convertRootToCard(root, context),\n warnings: context.warnings,\n };\n } catch {\n return {\n card: buildCard([]),\n warnings: [\n {\n code: \"dropped\",\n component: \"Root\",\n detail: \"Input could not be converted.\",\n },\n ],\n };\n }\n}\n"],"mappings":"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"}
|
|
@@ -48,11 +48,12 @@ function toTeamsAttachments(node, _options) {
|
|
|
48
48
|
usedInputIds: /* @__PURE__ */ new Set()
|
|
49
49
|
};
|
|
50
50
|
try {
|
|
51
|
-
const
|
|
51
|
+
const bounded = boundSpec(node, (reason) => warnings.push({
|
|
52
52
|
code: "clamped",
|
|
53
53
|
component: "Root",
|
|
54
54
|
detail: clampReasonDetail(reason)
|
|
55
|
-
}))
|
|
55
|
+
}));
|
|
56
|
+
const { root } = normalizeSpec(bounded);
|
|
56
57
|
const carouselCards = rootCarouselCards(root, context);
|
|
57
58
|
if (carouselCards !== void 0) {
|
|
58
59
|
const { items, truncated } = clampArray(carouselCards, 10);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toTeamsAttachments.js","names":[],"sources":["../../src/teams/toTeamsAttachments.ts"],"sourcesContent":["import {\n normalizeSpec,\n type NormalizedUIElement,\n type NormalizedUINode,\n} from \"../ir\";\nimport { boundSpec } from \"./boundSpec\";\nimport {\n CAROUSEL_ATTACHMENT_CAP,\n PAYLOAD_SOFT_CAP,\n buildAttachment,\n clampReasonDetail,\n utf8ByteLength,\n} from \"./constants\";\nimport {\n convertElement,\n convertRootToCard,\n type ConversionContext,\n} from \"./toAdaptiveCard\";\nimport type {\n TeamsAttachmentsResult,\n TeamsCardAttachment,\n TeamsConversionWarning,\n ToAdaptiveCardOptions,\n} from \"./types\";\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst isElement = (node: NormalizedUINode): node is NormalizedUIElement =>\n isRecord(node);\n\nconst normalizedList = (\n node: NormalizedUINode | undefined,\n): NormalizedUINode[] => {\n if (node === undefined || node === null) return [];\n if (!Array.isArray(node)) return [node];\n return node.flatMap((child) => normalizedList(child));\n};\n\nconst clampArray = <T>(\n value: readonly T[],\n cap: number,\n): { readonly items: T[]; readonly truncated: boolean } => ({\n items: value.slice(0, cap),\n truncated: value.length > cap,\n});\n\n/**\n * Recognizes a root that is a single `Carousel` element, returning its `Card`\n * children. Every child is routed through {@link convertElement} first (its\n * result is discarded) so an unknown or unsupported child still produces its\n * usual \"dropped\" warning instead of being filtered out silently; non-card\n * children are then dropped. `undefined` means the root is not exclusively a\n * root-level carousel, so the caller falls back to converting the whole tree\n * as one card.\n */\nfunction rootCarouselCards(\n root: NormalizedUINode | readonly NormalizedUINode[],\n context: ConversionContext,\n): NormalizedUIElement[] | undefined {\n const element = Array.isArray(root) && root.length === 1 ? root[0] : root;\n if (!isElement(element) || element.type !== \"Carousel\") return undefined;\n const cards: NormalizedUIElement[] = [];\n for (const child of normalizedList(element.children)) {\n if (!isElement(child)) continue;\n if (child.type === \"Card\") {\n cards.push(child);\n } else {\n convertElement(child, context, 1);\n }\n }\n return cards;\n}\n\n/**\n * Converts a generative-ui tree into Teams bot-framework message attachments.\n * A root-level `Carousel` becomes one attachment per `Card` child (clamped to\n * {@link CAROUSEL_ATTACHMENT_CAP}) with `attachmentLayout: \"carousel\"`;\n * anything else converts through {@link toAdaptiveCard}'s engine into a\n * single attachment. Never throws.\n */\nexport function toTeamsAttachments(\n node: unknown,\n _options?: ToAdaptiveCardOptions,\n): TeamsAttachmentsResult {\n const warnings: TeamsConversionWarning[] = [];\n const context: ConversionContext = { warnings, usedInputIds: new Set() };\n try {\n const bounded = boundSpec(node, (reason) =>\n warnings.push({\n code: \"clamped\",\n component: \"Root\",\n detail: clampReasonDetail(reason),\n }),\n );\n const { root } = normalizeSpec(bounded as never);\n const carouselCards = rootCarouselCards(root, context);\n if (carouselCards !== undefined) {\n const { items, truncated } = clampArray(\n carouselCards,\n CAROUSEL_ATTACHMENT_CAP,\n );\n if (truncated) {\n warnings.push({\n code: \"clamped\",\n component: \"Carousel\",\n detail: `cards were clamped to ${CAROUSEL_ATTACHMENT_CAP} entries.`,\n });\n }\n const attachments: TeamsCardAttachment[] = items.map((child) =>\n buildAttachment(convertRootToCard(child, context)),\n );\n const size = utf8ByteLength(JSON.stringify(attachments));\n if (size > PAYLOAD_SOFT_CAP) {\n warnings.push({\n code: \"clamped\",\n component: \"Carousel\",\n detail: `the carousel attachments total ${size} bytes, exceeding Teams' 100 KB bot message limit.`,\n });\n }\n return { attachments, attachmentLayout: \"carousel\", warnings };\n }\n const attachment = buildAttachment(convertRootToCard(root, context));\n return { attachments: [attachment], warnings };\n } catch {\n return {\n attachments: [],\n warnings: [\n {\n code: \"dropped\",\n component: \"Root\",\n detail: \"Input could not be converted.\",\n },\n ],\n };\n }\n}\n"],"mappings":";;;;;AAyBA,MAAM,YAAY,UAChB,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAErE,MAAM,aAAa,SACjB,SAAS,IAAI;AAEf,MAAM,kBACJ,SACuB;CACvB,IAAI,SAAS,KAAA,KAAa,SAAS,MAAM,OAAO,CAAC;CACjD,IAAI,CAAC,MAAM,QAAQ,IAAI,GAAG,OAAO,CAAC,IAAI;CACtC,OAAO,KAAK,SAAS,UAAU,eAAe,KAAK,CAAC;AACtD;AAEA,MAAM,cACJ,OACA,SAC0D;CAC1D,OAAO,MAAM,MAAM,GAAG,GAAG;CACzB,WAAW,MAAM,SAAS;AAC5B;;;;;;;;;;AAWA,SAAS,kBACP,MACA,SACmC;CACnC,MAAM,UAAU,MAAM,QAAQ,IAAI,KAAK,KAAK,WAAW,IAAI,KAAK,KAAK;CACrE,IAAI,CAAC,UAAU,OAAO,KAAK,QAAQ,SAAS,YAAY,OAAO,KAAA;CAC/D,MAAM,QAA+B,CAAC;CACtC,KAAK,MAAM,SAAS,eAAe,QAAQ,QAAQ,GAAG;EACpD,IAAI,CAAC,UAAU,KAAK,GAAG;EACvB,IAAI,MAAM,SAAS,QACjB,MAAM,KAAK,KAAK;OAEhB,eAAe,OAAO,SAAS,CAAC;CAEpC;CACA,OAAO;AACT;;;;;;;;AASA,SAAgB,mBACd,MACA,UACwB;CACxB,MAAM,WAAqC,CAAC;CAC5C,MAAM,UAA6B;EAAE;EAAU,8BAAc,IAAI,IAAI;CAAE;CACvE,IAAI;
|
|
1
|
+
{"version":3,"file":"toTeamsAttachments.js","names":[],"sources":["../../src/teams/toTeamsAttachments.ts"],"sourcesContent":["import {\n normalizeSpec,\n type NormalizedUIElement,\n type NormalizedUINode,\n} from \"../ir\";\nimport { boundSpec } from \"./boundSpec\";\nimport {\n CAROUSEL_ATTACHMENT_CAP,\n PAYLOAD_SOFT_CAP,\n buildAttachment,\n clampReasonDetail,\n utf8ByteLength,\n} from \"./constants\";\nimport {\n convertElement,\n convertRootToCard,\n type ConversionContext,\n} from \"./toAdaptiveCard\";\nimport type {\n TeamsAttachmentsResult,\n TeamsCardAttachment,\n TeamsConversionWarning,\n ToAdaptiveCardOptions,\n} from \"./types\";\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst isElement = (node: NormalizedUINode): node is NormalizedUIElement =>\n isRecord(node);\n\nconst normalizedList = (\n node: NormalizedUINode | undefined,\n): NormalizedUINode[] => {\n if (node === undefined || node === null) return [];\n if (!Array.isArray(node)) return [node];\n return node.flatMap((child) => normalizedList(child));\n};\n\nconst clampArray = <T>(\n value: readonly T[],\n cap: number,\n): { readonly items: T[]; readonly truncated: boolean } => ({\n items: value.slice(0, cap),\n truncated: value.length > cap,\n});\n\n/**\n * Recognizes a root that is a single `Carousel` element, returning its `Card`\n * children. Every child is routed through {@link convertElement} first (its\n * result is discarded) so an unknown or unsupported child still produces its\n * usual \"dropped\" warning instead of being filtered out silently; non-card\n * children are then dropped. `undefined` means the root is not exclusively a\n * root-level carousel, so the caller falls back to converting the whole tree\n * as one card.\n */\nfunction rootCarouselCards(\n root: NormalizedUINode | readonly NormalizedUINode[],\n context: ConversionContext,\n): NormalizedUIElement[] | undefined {\n const element = Array.isArray(root) && root.length === 1 ? root[0] : root;\n if (!isElement(element) || element.type !== \"Carousel\") return undefined;\n const cards: NormalizedUIElement[] = [];\n for (const child of normalizedList(element.children)) {\n if (!isElement(child)) continue;\n if (child.type === \"Card\") {\n cards.push(child);\n } else {\n convertElement(child, context, 1);\n }\n }\n return cards;\n}\n\n/**\n * Converts a generative-ui tree into Teams bot-framework message attachments.\n * A root-level `Carousel` becomes one attachment per `Card` child (clamped to\n * {@link CAROUSEL_ATTACHMENT_CAP}) with `attachmentLayout: \"carousel\"`;\n * anything else converts through {@link toAdaptiveCard}'s engine into a\n * single attachment. Never throws.\n */\nexport function toTeamsAttachments(\n node: unknown,\n _options?: ToAdaptiveCardOptions,\n): TeamsAttachmentsResult {\n const warnings: TeamsConversionWarning[] = [];\n const context: ConversionContext = { warnings, usedInputIds: new Set() };\n try {\n const bounded = boundSpec(node, (reason) =>\n warnings.push({\n code: \"clamped\",\n component: \"Root\",\n detail: clampReasonDetail(reason),\n }),\n );\n const { root } = normalizeSpec(bounded as never);\n const carouselCards = rootCarouselCards(root, context);\n if (carouselCards !== undefined) {\n const { items, truncated } = clampArray(\n carouselCards,\n CAROUSEL_ATTACHMENT_CAP,\n );\n if (truncated) {\n warnings.push({\n code: \"clamped\",\n component: \"Carousel\",\n detail: `cards were clamped to ${CAROUSEL_ATTACHMENT_CAP} entries.`,\n });\n }\n const attachments: TeamsCardAttachment[] = items.map((child) =>\n buildAttachment(convertRootToCard(child, context)),\n );\n const size = utf8ByteLength(JSON.stringify(attachments));\n if (size > PAYLOAD_SOFT_CAP) {\n warnings.push({\n code: \"clamped\",\n component: \"Carousel\",\n detail: `the carousel attachments total ${size} bytes, exceeding Teams' 100 KB bot message limit.`,\n });\n }\n return { attachments, attachmentLayout: \"carousel\", warnings };\n }\n const attachment = buildAttachment(convertRootToCard(root, context));\n return { attachments: [attachment], warnings };\n } catch {\n return {\n attachments: [],\n warnings: [\n {\n code: \"dropped\",\n component: \"Root\",\n detail: \"Input could not be converted.\",\n },\n ],\n };\n }\n}\n"],"mappings":";;;;;AAyBA,MAAM,YAAY,UAChB,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAErE,MAAM,aAAa,SACjB,SAAS,IAAI;AAEf,MAAM,kBACJ,SACuB;CACvB,IAAI,SAAS,KAAA,KAAa,SAAS,MAAM,OAAO,CAAC;CACjD,IAAI,CAAC,MAAM,QAAQ,IAAI,GAAG,OAAO,CAAC,IAAI;CACtC,OAAO,KAAK,SAAS,UAAU,eAAe,KAAK,CAAC;AACtD;AAEA,MAAM,cACJ,OACA,SAC0D;CAC1D,OAAO,MAAM,MAAM,GAAG,GAAG;CACzB,WAAW,MAAM,SAAS;AAC5B;;;;;;;;;;AAWA,SAAS,kBACP,MACA,SACmC;CACnC,MAAM,UAAU,MAAM,QAAQ,IAAI,KAAK,KAAK,WAAW,IAAI,KAAK,KAAK;CACrE,IAAI,CAAC,UAAU,OAAO,KAAK,QAAQ,SAAS,YAAY,OAAO,KAAA;CAC/D,MAAM,QAA+B,CAAC;CACtC,KAAK,MAAM,SAAS,eAAe,QAAQ,QAAQ,GAAG;EACpD,IAAI,CAAC,UAAU,KAAK,GAAG;EACvB,IAAI,MAAM,SAAS,QACjB,MAAM,KAAK,KAAK;OAEhB,eAAe,OAAO,SAAS,CAAC;CAEpC;CACA,OAAO;AACT;;;;;;;;AASA,SAAgB,mBACd,MACA,UACwB;CACxB,MAAM,WAAqC,CAAC;CAC5C,MAAM,UAA6B;EAAE;EAAU,8BAAc,IAAI,IAAI;CAAE;CACvE,IAAI;EACF,MAAM,UAAU,UAAU,OAAO,WAC/B,SAAS,KAAK;GACZ,MAAM;GACN,WAAW;GACX,QAAQ,kBAAkB,MAAM;EAClC,CAAC,CACH;EACA,MAAM,EAAE,SAAS,cAAc,OAAgB;EAC/C,MAAM,gBAAgB,kBAAkB,MAAM,OAAO;EACrD,IAAI,kBAAkB,KAAA,GAAW;GAC/B,MAAM,EAAE,OAAO,cAAc,WAC3B,eAAA,EAEF;GACA,IAAI,WACF,SAAS,KAAK;IACZ,MAAM;IACN,WAAW;IACX,QAAQ;GACV,CAAC;GAEH,MAAM,cAAqC,MAAM,KAAK,UACpD,gBAAgB,kBAAkB,OAAO,OAAO,CAAC,CACnD;GACA,MAAM,OAAO,eAAe,KAAK,UAAU,WAAW,CAAC;GACvD,IAAI,OAAA,KACF,SAAS,KAAK;IACZ,MAAM;IACN,WAAW;IACX,QAAQ,kCAAkC,KAAK;GACjD,CAAC;GAEH,OAAO;IAAE;IAAa,kBAAkB;IAAY;GAAS;EAC/D;EAEA,OAAO;GAAE,aAAa,CADH,gBAAgB,kBAAkB,MAAM,OAAO,CAClC,CAAC;GAAG;EAAS;CAC/C,QAAQ;EACN,OAAO;GACL,aAAa,CAAC;GACd,UAAU,CACR;IACE,MAAM;IACN,WAAW;IACX,QAAQ;GACV,CACF;EACF;CACF;AACF"}
|
package/dist/vocabulary/data.js
CHANGED
|
@@ -78,7 +78,7 @@ const TICK_COUNT = 5;
|
|
|
78
78
|
/** `TICK_COUNT` evenly spaced ticks from `max` down to `0`, for a top-to-bottom y-axis column. */
|
|
79
79
|
function tickValues(max) {
|
|
80
80
|
const ticks = [];
|
|
81
|
-
for (let i = 0; i < TICK_COUNT; i++) ticks.push(max * (
|
|
81
|
+
for (let i = 0; i < TICK_COUNT; i++) ticks.push(max * (4 - i) / 4);
|
|
82
82
|
return ticks;
|
|
83
83
|
}
|
|
84
84
|
function formatTick(value) {
|
|
@@ -97,8 +97,9 @@ function renderSeriesMarks(variant, series, count, scaleMax, stacked) {
|
|
|
97
97
|
const marks = s.values.map((value, i) => {
|
|
98
98
|
const height = scaleMax > 0 ? value / scaleMax * CHART_HEIGHT : 0;
|
|
99
99
|
const belowHeight = stacked && scaleMax > 0 ? (cumulative[i] ?? 0) / scaleMax * CHART_HEIGHT : 0;
|
|
100
|
+
const x = stacked || seriesCount <= 1 ? i * slot + gap / 2 : i * slot + gap / 2 + seriesIndex * perSeriesWidth;
|
|
100
101
|
return /* @__PURE__ */ jsx("rect", {
|
|
101
|
-
x
|
|
102
|
+
x,
|
|
102
103
|
y: CHART_HEIGHT - belowHeight - height,
|
|
103
104
|
width: perSeriesWidth,
|
|
104
105
|
height,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data.js","names":[],"sources":["../../src/vocabulary/data.tsx"],"sourcesContent":["import { z } from \"zod\";\nimport type { GenerativeUILibrary } from \"../types\";\n\nconst columnSchema = z.object({\n label: z.string().describe(\"Column header label.\"),\n});\n\nconst cellSchema = z\n .union([z.string(), z.number(), z.boolean()])\n .describe(\"A cell value.\");\n\nconst CHART_HEIGHT = 40;\nconst CHART_WIDTH = 100;\n\nconst clampValue = (value: unknown): number =>\n typeof value === \"number\" && Number.isFinite(value) ? Math.max(0, value) : 0;\n\nconst yFor = (value: number, max: number): number =>\n max > 0 ? CHART_HEIGHT - (value / max) * CHART_HEIGHT : CHART_HEIGHT;\n\nconst pointSchema = z.object({\n label: z.string().optional().describe(\"Point label.\"),\n value: z.number().describe(\"Point value.\"),\n});\n\nconst seriesSchema = z.object({\n label: z.string().optional().describe(\"Series label, shown in the legend.\"),\n data: z.array(pointSchema).describe(\"Data points for this series.\"),\n});\n\ntype ChartPoint = { label?: string; value: number };\ntype ChartSeriesInput = { label?: string; data: ChartPoint[] };\ntype NormalizedChartSeries = {\n label: string | undefined;\n values: number[];\n labels: (string | undefined)[];\n};\n\n/** Pads every series to the longest series' length, treating a shorter series' missing points as `{ value: 0 }` so mismatched series never misalign or throw. */\nfunction normalizeSeries(\n seriesProp: unknown,\n dataProp: unknown,\n): { count: number; series: NormalizedChartSeries[] } {\n const rawSeries: ChartSeriesInput[] =\n Array.isArray(seriesProp) && seriesProp.length > 0\n ? (seriesProp as ChartSeriesInput[])\n : [{ data: Array.isArray(dataProp) ? (dataProp as ChartPoint[]) : [] }];\n\n const count = rawSeries.reduce((max, s) => {\n const len = Array.isArray(s?.data) ? s.data.length : 0;\n return Math.max(max, len);\n }, 0);\n\n const series = rawSeries.map((s) => {\n const points = Array.isArray(s?.data) ? s.data : [];\n const values: number[] = [];\n const labels: (string | undefined)[] = [];\n for (let i = 0; i < count; i++) {\n const point = points[i];\n values.push(clampValue(point?.value));\n labels.push(typeof point?.label === \"string\" ? point.label : undefined);\n }\n return {\n label: typeof s?.label === \"string\" ? s.label : undefined,\n values,\n labels,\n };\n });\n\n return { count, series };\n}\n\nfunction computeMax(\n series: NormalizedChartSeries[],\n count: number,\n stacked: boolean,\n): number {\n if (stacked) {\n let max = 0;\n for (let i = 0; i < count; i++) {\n let sum = 0;\n for (const s of series) sum += s.values[i] ?? 0;\n max = Math.max(max, sum);\n }\n return max;\n }\n let max = 0;\n for (const s of series) for (const v of s.values) max = Math.max(max, v);\n return max;\n}\n\nconst NICE_MULTIPLES = [1, 2, 5];\n\n/** The smallest of `{1, 2, 5} × 10^n` that is `>= max`, so axis ticks land on round numbers. */\nfunction niceMax(max: number): number {\n if (max <= 0) return 0;\n const exponent = Math.floor(Math.log10(max));\n for (const multiple of NICE_MULTIPLES) {\n const candidate = multiple * 10 ** exponent;\n if (candidate >= max) return candidate;\n }\n return 10 ** (exponent + 1);\n}\n\nconst TICK_COUNT = 5;\n\n/** `TICK_COUNT` evenly spaced ticks from `max` down to `0`, for a top-to-bottom y-axis column. */\nfunction tickValues(max: number): number[] {\n const ticks: number[] = [];\n for (let i = 0; i < TICK_COUNT; i++) {\n ticks.push((max * (TICK_COUNT - 1 - i)) / (TICK_COUNT - 1));\n }\n return ticks;\n}\n\nfunction formatTick(value: number): string {\n return (Math.round(value * 100) / 100).toLocaleString(\"en-US\");\n}\n\ntype ChartVariant = \"bar\" | \"line\" | \"sparkline\" | \"area\";\n\nconst SERIES_PALETTE_SIZE = 5;\n\nfunction renderSeriesMarks(\n variant: ChartVariant,\n series: NormalizedChartSeries[],\n count: number,\n scaleMax: number,\n stacked: boolean,\n) {\n if (variant === \"bar\") {\n const slot = count > 0 ? CHART_WIDTH / count : 0;\n const gap = slot * 0.2;\n const groupWidth = slot - gap;\n const seriesCount = series.length;\n const perSeriesWidth =\n stacked || seriesCount <= 1 ? groupWidth : groupWidth / seriesCount;\n const cumulative = new Array(count).fill(0) as number[];\n\n return series.map((s, seriesIndex) => {\n const marks = s.values.map((value, i) => {\n const height = scaleMax > 0 ? (value / scaleMax) * CHART_HEIGHT : 0;\n const belowHeight =\n stacked && scaleMax > 0\n ? ((cumulative[i] ?? 0) / scaleMax) * CHART_HEIGHT\n : 0;\n const x =\n stacked || seriesCount <= 1\n ? i * slot + gap / 2\n : i * slot + gap / 2 + seriesIndex * perSeriesWidth;\n return (\n <rect\n key={i}\n x={x}\n y={CHART_HEIGHT - belowHeight - height}\n width={perSeriesWidth}\n height={height}\n fill=\"currentColor\"\n />\n );\n });\n if (stacked) {\n for (let i = 0; i < count; i++) {\n cumulative[i] = (cumulative[i] ?? 0) + (s.values[i] ?? 0);\n }\n }\n return (\n <g\n key={seriesIndex}\n data-aui=\"chart-series\"\n data-aui-series={seriesIndex % SERIES_PALETTE_SIZE}\n >\n {marks}\n </g>\n );\n });\n }\n\n if (variant === \"area\") {\n const cumulative = new Array(count).fill(0) as number[];\n\n return series.map((s, seriesIndex) => {\n const bottoms = stacked\n ? cumulative.slice()\n : (new Array(count).fill(0) as number[]);\n if (stacked) {\n for (let i = 0; i < count; i++) {\n cumulative[i] = (cumulative[i] ?? 0) + (s.values[i] ?? 0);\n }\n }\n const tops = stacked ? cumulative.slice() : s.values.slice();\n\n if (count === 1) {\n const y = yFor(tops[0] ?? 0, scaleMax);\n return (\n <g\n key={seriesIndex}\n data-aui=\"chart-series\"\n data-aui-series={seriesIndex % SERIES_PALETTE_SIZE}\n >\n <circle cx={CHART_WIDTH / 2} cy={y} r={2} fill=\"currentColor\" />\n </g>\n );\n }\n if (count === 0) {\n return (\n <g\n key={seriesIndex}\n data-aui=\"chart-series\"\n data-aui-series={seriesIndex % SERIES_PALETTE_SIZE}\n />\n );\n }\n\n const topPoints = tops.map(\n (v, i) => `${(i / (count - 1)) * CHART_WIDTH},${yFor(v, scaleMax)}`,\n );\n const bottomPoints = bottoms\n .map(\n (v, i) => `${(i / (count - 1)) * CHART_WIDTH},${yFor(v, scaleMax)}`,\n )\n .reverse();\n\n return (\n <g\n key={seriesIndex}\n data-aui=\"chart-series\"\n data-aui-series={seriesIndex % SERIES_PALETTE_SIZE}\n >\n <polygon\n points={[...topPoints, ...bottomPoints].join(\" \")}\n fill=\"currentColor\"\n fillOpacity=\"0.25\"\n />\n <polyline\n points={topPoints.join(\" \")}\n fill=\"none\"\n stroke=\"currentColor\"\n vectorEffect=\"non-scaling-stroke\"\n />\n </g>\n );\n });\n }\n\n // line / sparkline: each series is always drawn independently. Stacking a line has no single standard meaning, so `stacked` is ignored (the caller never passes it for these variants).\n return series.map((s, seriesIndex) => (\n <g\n key={seriesIndex}\n data-aui=\"chart-series\"\n data-aui-series={seriesIndex % SERIES_PALETTE_SIZE}\n >\n {count === 1 ? (\n <circle\n cx={CHART_WIDTH / 2}\n cy={yFor(s.values[0] ?? 0, scaleMax)}\n r={2}\n fill=\"currentColor\"\n />\n ) : count > 1 ? (\n <polyline\n points={s.values\n .map(\n (v, i) =>\n `${(i / (count - 1)) * CHART_WIDTH},${yFor(v, scaleMax)}`,\n )\n .join(\" \")}\n fill=\"none\"\n stroke=\"currentColor\"\n vectorEffect=\"non-scaling-stroke\"\n />\n ) : null}\n </g>\n ));\n}\n\nexport const dataVocabulary = {\n Table: {\n description:\n \"Tabular data. Provide `columns` and `rows`; each row is an array of cells matching the columns.\",\n properties: z.object({\n columns: z.array(columnSchema).optional().describe(\"Column definitions.\"),\n rows: z.array(z.array(cellSchema)).optional().describe(\"Rows of cells.\"),\n }),\n render: ({ columns, rows, children }) => (\n <table data-aui=\"table\">\n {columns?.length ? (\n <thead>\n <tr>\n {columns.map((c: { label: string }, i: number) => (\n <th key={i} data-aui=\"table-col\">\n {c.label}\n </th>\n ))}\n </tr>\n </thead>\n ) : null}\n {rows?.length ? (\n <tbody>\n {rows.map((row: (string | number | boolean)[], r: number) => (\n <tr key={r}>\n {row.map((cell: string | number | boolean, c: number) => (\n <td key={c}>{String(cell)}</td>\n ))}\n </tr>\n ))}\n </tbody>\n ) : null}\n {children}\n </table>\n ),\n },\n Markdown: {\n description:\n \"A markdown string. Rendered as-is by default; override this component for a full markdown renderer.\",\n properties: z.object({\n value: z.string().describe(\"Markdown source.\"),\n }),\n streamProperties: true,\n render: ({ value, children }) => (\n <div data-aui=\"markdown\">\n {value}\n {children}\n </div>\n ),\n },\n Chart: {\n description:\n \"A chart. `variant` selects bar, line, sparkline, or area rendering. Provide `data` for a single series, or `series` for multiple named series (`series` takes precedence over `data` when both are present). Set `stacked` to accumulate series values instead of overlaying them, and `showAxis`/`showLegend` to add y-axis ticks, x-axis point labels, and a legend.\",\n properties: z.object({\n variant: z\n .enum([\"bar\", \"line\", \"sparkline\", \"area\"])\n .describe(\"Chart variant.\"),\n data: z\n .array(pointSchema)\n .optional()\n .describe(\"Data points for a single series.\"),\n series: z\n .array(seriesSchema)\n .optional()\n .describe(\n \"Multiple named series; takes precedence over `data` when present.\",\n ),\n stacked: z\n .boolean()\n .optional()\n .describe(\n \"Accumulate series values instead of overlaying them (bar and area variants; ignored for line and sparkline).\",\n ),\n showAxis: z\n .boolean()\n .optional()\n .describe(\"Show y-axis ticks and x-axis point labels.\"),\n showLegend: z\n .boolean()\n .optional()\n .describe(\"Show a legend mapping each series to its color.\"),\n color: z\n .string()\n .optional()\n .describe(\n \"Series color (single-series mode only). One of `emphasis`, `secondary`, `alpha-70`, `white`, `white-70`, or `white-50`, matching Text's `color` tokens; other values have no visual effect.\",\n ),\n }),\n render: ({\n variant,\n data,\n series,\n stacked,\n showAxis,\n showLegend,\n color,\n }) => {\n const usesExtendedFeatures =\n variant === \"area\" ||\n !!stacked ||\n !!showAxis ||\n !!showLegend ||\n (Array.isArray(series) && series.length > 0);\n\n if (!usesExtendedFeatures) {\n const points = Array.isArray(data) ? data : [];\n const n = points.length;\n const values = points.map((d) => clampValue(d?.value));\n const max = values.reduce((m, v) => Math.max(m, v), 0);\n const slot = n > 0 ? CHART_WIDTH / n : 0;\n const gap = slot * 0.2;\n const barWidth = slot - gap;\n\n return (\n <svg\n data-aui=\"chart\"\n data-aui-variant={variant}\n data-aui-color={color}\n role=\"img\"\n aria-label={`${variant} chart with ${n} data points`}\n viewBox={`0 0 ${CHART_WIDTH} ${CHART_HEIGHT}`}\n preserveAspectRatio=\"none\"\n >\n {variant === \"bar\" ? (\n values.map((v, i) => {\n const height = max > 0 ? (v / max) * CHART_HEIGHT : 0;\n return (\n <rect\n key={i}\n x={i * slot + gap / 2}\n y={CHART_HEIGHT - height}\n width={barWidth}\n height={height}\n fill=\"currentColor\"\n />\n );\n })\n ) : n === 1 ? (\n <circle\n cx={CHART_WIDTH / 2}\n cy={yFor(values[0] ?? 0, max)}\n r={2}\n fill=\"currentColor\"\n />\n ) : n > 1 ? (\n <polyline\n points={values\n .map(\n (v, i) => `${(i / (n - 1)) * CHART_WIDTH},${yFor(v, max)}`,\n )\n .join(\" \")}\n fill=\"none\"\n stroke=\"currentColor\"\n />\n ) : null}\n </svg>\n );\n }\n\n const { count, series: normalized } = normalizeSeries(series, data);\n const stackedScale =\n !!stacked && (variant === \"bar\" || variant === \"area\");\n const rawMax = computeMax(normalized, count, stackedScale);\n const scaleMax = showAxis ? niceMax(rawMax) : rawMax;\n const marks = renderSeriesMarks(\n variant,\n normalized,\n count,\n scaleMax,\n stackedScale,\n );\n\n const svg = (\n <svg\n data-aui=\"chart\"\n data-aui-variant={variant}\n data-aui-color={color}\n role=\"img\"\n aria-label={`${variant} chart with ${count} data points`}\n viewBox={`0 0 ${CHART_WIDTH} ${CHART_HEIGHT}`}\n preserveAspectRatio=\"none\"\n >\n {marks}\n </svg>\n );\n\n if (!showAxis && !showLegend) return svg;\n\n return (\n <div data-aui=\"chart-frame\">\n {showAxis ? (\n <div data-aui=\"chart-ticks\">\n {tickValues(scaleMax).map((tick, i) => (\n <div key={i}>{formatTick(tick)}</div>\n ))}\n </div>\n ) : null}\n {svg}\n {showAxis ? (\n <div data-aui=\"chart-xlabels\">\n {(normalized[0]?.labels ?? []).map((label, i) => (\n <div key={i}>{label ?? \"\"}</div>\n ))}\n </div>\n ) : null}\n {showLegend ? (\n <div data-aui=\"chart-legend\">\n {normalized.map((s, i) => (\n <span\n key={i}\n data-aui=\"chart-legend-item\"\n data-aui-series={i % SERIES_PALETTE_SIZE}\n >\n <span data-aui=\"chart-legend-swatch\" />\n {s.label ?? `Series ${i + 1}`}\n </span>\n ))}\n </div>\n ) : null}\n </div>\n );\n },\n },\n} satisfies GenerativeUILibrary;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"data.js","names":[],"sources":["../../src/vocabulary/data.tsx"],"sourcesContent":["import { z } from \"zod\";\nimport type { GenerativeUILibrary } from \"../types\";\n\nconst columnSchema = z.object({\n label: z.string().describe(\"Column header label.\"),\n});\n\nconst cellSchema = z\n .union([z.string(), z.number(), z.boolean()])\n .describe(\"A cell value.\");\n\nconst CHART_HEIGHT = 40;\nconst CHART_WIDTH = 100;\n\nconst clampValue = (value: unknown): number =>\n typeof value === \"number\" && Number.isFinite(value) ? Math.max(0, value) : 0;\n\nconst yFor = (value: number, max: number): number =>\n max > 0 ? CHART_HEIGHT - (value / max) * CHART_HEIGHT : CHART_HEIGHT;\n\nconst pointSchema = z.object({\n label: z.string().optional().describe(\"Point label.\"),\n value: z.number().describe(\"Point value.\"),\n});\n\nconst seriesSchema = z.object({\n label: z.string().optional().describe(\"Series label, shown in the legend.\"),\n data: z.array(pointSchema).describe(\"Data points for this series.\"),\n});\n\ntype ChartPoint = { label?: string; value: number };\ntype ChartSeriesInput = { label?: string; data: ChartPoint[] };\ntype NormalizedChartSeries = {\n label: string | undefined;\n values: number[];\n labels: (string | undefined)[];\n};\n\n/** Pads every series to the longest series' length, treating a shorter series' missing points as `{ value: 0 }` so mismatched series never misalign or throw. */\nfunction normalizeSeries(\n seriesProp: unknown,\n dataProp: unknown,\n): { count: number; series: NormalizedChartSeries[] } {\n const rawSeries: ChartSeriesInput[] =\n Array.isArray(seriesProp) && seriesProp.length > 0\n ? (seriesProp as ChartSeriesInput[])\n : [{ data: Array.isArray(dataProp) ? (dataProp as ChartPoint[]) : [] }];\n\n const count = rawSeries.reduce((max, s) => {\n const len = Array.isArray(s?.data) ? s.data.length : 0;\n return Math.max(max, len);\n }, 0);\n\n const series = rawSeries.map((s) => {\n const points = Array.isArray(s?.data) ? s.data : [];\n const values: number[] = [];\n const labels: (string | undefined)[] = [];\n for (let i = 0; i < count; i++) {\n const point = points[i];\n values.push(clampValue(point?.value));\n labels.push(typeof point?.label === \"string\" ? point.label : undefined);\n }\n return {\n label: typeof s?.label === \"string\" ? s.label : undefined,\n values,\n labels,\n };\n });\n\n return { count, series };\n}\n\nfunction computeMax(\n series: NormalizedChartSeries[],\n count: number,\n stacked: boolean,\n): number {\n if (stacked) {\n let max = 0;\n for (let i = 0; i < count; i++) {\n let sum = 0;\n for (const s of series) sum += s.values[i] ?? 0;\n max = Math.max(max, sum);\n }\n return max;\n }\n let max = 0;\n for (const s of series) for (const v of s.values) max = Math.max(max, v);\n return max;\n}\n\nconst NICE_MULTIPLES = [1, 2, 5];\n\n/** The smallest of `{1, 2, 5} × 10^n` that is `>= max`, so axis ticks land on round numbers. */\nfunction niceMax(max: number): number {\n if (max <= 0) return 0;\n const exponent = Math.floor(Math.log10(max));\n for (const multiple of NICE_MULTIPLES) {\n const candidate = multiple * 10 ** exponent;\n if (candidate >= max) return candidate;\n }\n return 10 ** (exponent + 1);\n}\n\nconst TICK_COUNT = 5;\n\n/** `TICK_COUNT` evenly spaced ticks from `max` down to `0`, for a top-to-bottom y-axis column. */\nfunction tickValues(max: number): number[] {\n const ticks: number[] = [];\n for (let i = 0; i < TICK_COUNT; i++) {\n ticks.push((max * (TICK_COUNT - 1 - i)) / (TICK_COUNT - 1));\n }\n return ticks;\n}\n\nfunction formatTick(value: number): string {\n return (Math.round(value * 100) / 100).toLocaleString(\"en-US\");\n}\n\ntype ChartVariant = \"bar\" | \"line\" | \"sparkline\" | \"area\";\n\nconst SERIES_PALETTE_SIZE = 5;\n\nfunction renderSeriesMarks(\n variant: ChartVariant,\n series: NormalizedChartSeries[],\n count: number,\n scaleMax: number,\n stacked: boolean,\n) {\n if (variant === \"bar\") {\n const slot = count > 0 ? CHART_WIDTH / count : 0;\n const gap = slot * 0.2;\n const groupWidth = slot - gap;\n const seriesCount = series.length;\n const perSeriesWidth =\n stacked || seriesCount <= 1 ? groupWidth : groupWidth / seriesCount;\n const cumulative = new Array(count).fill(0) as number[];\n\n return series.map((s, seriesIndex) => {\n const marks = s.values.map((value, i) => {\n const height = scaleMax > 0 ? (value / scaleMax) * CHART_HEIGHT : 0;\n const belowHeight =\n stacked && scaleMax > 0\n ? ((cumulative[i] ?? 0) / scaleMax) * CHART_HEIGHT\n : 0;\n const x =\n stacked || seriesCount <= 1\n ? i * slot + gap / 2\n : i * slot + gap / 2 + seriesIndex * perSeriesWidth;\n return (\n <rect\n key={i}\n x={x}\n y={CHART_HEIGHT - belowHeight - height}\n width={perSeriesWidth}\n height={height}\n fill=\"currentColor\"\n />\n );\n });\n if (stacked) {\n for (let i = 0; i < count; i++) {\n cumulative[i] = (cumulative[i] ?? 0) + (s.values[i] ?? 0);\n }\n }\n return (\n <g\n key={seriesIndex}\n data-aui=\"chart-series\"\n data-aui-series={seriesIndex % SERIES_PALETTE_SIZE}\n >\n {marks}\n </g>\n );\n });\n }\n\n if (variant === \"area\") {\n const cumulative = new Array(count).fill(0) as number[];\n\n return series.map((s, seriesIndex) => {\n const bottoms = stacked\n ? cumulative.slice()\n : (new Array(count).fill(0) as number[]);\n if (stacked) {\n for (let i = 0; i < count; i++) {\n cumulative[i] = (cumulative[i] ?? 0) + (s.values[i] ?? 0);\n }\n }\n const tops = stacked ? cumulative.slice() : s.values.slice();\n\n if (count === 1) {\n const y = yFor(tops[0] ?? 0, scaleMax);\n return (\n <g\n key={seriesIndex}\n data-aui=\"chart-series\"\n data-aui-series={seriesIndex % SERIES_PALETTE_SIZE}\n >\n <circle cx={CHART_WIDTH / 2} cy={y} r={2} fill=\"currentColor\" />\n </g>\n );\n }\n if (count === 0) {\n return (\n <g\n key={seriesIndex}\n data-aui=\"chart-series\"\n data-aui-series={seriesIndex % SERIES_PALETTE_SIZE}\n />\n );\n }\n\n const topPoints = tops.map(\n (v, i) => `${(i / (count - 1)) * CHART_WIDTH},${yFor(v, scaleMax)}`,\n );\n const bottomPoints = bottoms\n .map(\n (v, i) => `${(i / (count - 1)) * CHART_WIDTH},${yFor(v, scaleMax)}`,\n )\n .reverse();\n\n return (\n <g\n key={seriesIndex}\n data-aui=\"chart-series\"\n data-aui-series={seriesIndex % SERIES_PALETTE_SIZE}\n >\n <polygon\n points={[...topPoints, ...bottomPoints].join(\" \")}\n fill=\"currentColor\"\n fillOpacity=\"0.25\"\n />\n <polyline\n points={topPoints.join(\" \")}\n fill=\"none\"\n stroke=\"currentColor\"\n vectorEffect=\"non-scaling-stroke\"\n />\n </g>\n );\n });\n }\n\n // line / sparkline: each series is always drawn independently. Stacking a line has no single standard meaning, so `stacked` is ignored (the caller never passes it for these variants).\n return series.map((s, seriesIndex) => (\n <g\n key={seriesIndex}\n data-aui=\"chart-series\"\n data-aui-series={seriesIndex % SERIES_PALETTE_SIZE}\n >\n {count === 1 ? (\n <circle\n cx={CHART_WIDTH / 2}\n cy={yFor(s.values[0] ?? 0, scaleMax)}\n r={2}\n fill=\"currentColor\"\n />\n ) : count > 1 ? (\n <polyline\n points={s.values\n .map(\n (v, i) =>\n `${(i / (count - 1)) * CHART_WIDTH},${yFor(v, scaleMax)}`,\n )\n .join(\" \")}\n fill=\"none\"\n stroke=\"currentColor\"\n vectorEffect=\"non-scaling-stroke\"\n />\n ) : null}\n </g>\n ));\n}\n\nexport const dataVocabulary = {\n Table: {\n description:\n \"Tabular data. Provide `columns` and `rows`; each row is an array of cells matching the columns.\",\n properties: z.object({\n columns: z.array(columnSchema).optional().describe(\"Column definitions.\"),\n rows: z.array(z.array(cellSchema)).optional().describe(\"Rows of cells.\"),\n }),\n render: ({ columns, rows, children }) => (\n <table data-aui=\"table\">\n {columns?.length ? (\n <thead>\n <tr>\n {columns.map((c: { label: string }, i: number) => (\n <th key={i} data-aui=\"table-col\">\n {c.label}\n </th>\n ))}\n </tr>\n </thead>\n ) : null}\n {rows?.length ? (\n <tbody>\n {rows.map((row: (string | number | boolean)[], r: number) => (\n <tr key={r}>\n {row.map((cell: string | number | boolean, c: number) => (\n <td key={c}>{String(cell)}</td>\n ))}\n </tr>\n ))}\n </tbody>\n ) : null}\n {children}\n </table>\n ),\n },\n Markdown: {\n description:\n \"A markdown string. Rendered as-is by default; override this component for a full markdown renderer.\",\n properties: z.object({\n value: z.string().describe(\"Markdown source.\"),\n }),\n streamProperties: true,\n render: ({ value, children }) => (\n <div data-aui=\"markdown\">\n {value}\n {children}\n </div>\n ),\n },\n Chart: {\n description:\n \"A chart. `variant` selects bar, line, sparkline, or area rendering. Provide `data` for a single series, or `series` for multiple named series (`series` takes precedence over `data` when both are present). Set `stacked` to accumulate series values instead of overlaying them, and `showAxis`/`showLegend` to add y-axis ticks, x-axis point labels, and a legend.\",\n properties: z.object({\n variant: z\n .enum([\"bar\", \"line\", \"sparkline\", \"area\"])\n .describe(\"Chart variant.\"),\n data: z\n .array(pointSchema)\n .optional()\n .describe(\"Data points for a single series.\"),\n series: z\n .array(seriesSchema)\n .optional()\n .describe(\n \"Multiple named series; takes precedence over `data` when present.\",\n ),\n stacked: z\n .boolean()\n .optional()\n .describe(\n \"Accumulate series values instead of overlaying them (bar and area variants; ignored for line and sparkline).\",\n ),\n showAxis: z\n .boolean()\n .optional()\n .describe(\"Show y-axis ticks and x-axis point labels.\"),\n showLegend: z\n .boolean()\n .optional()\n .describe(\"Show a legend mapping each series to its color.\"),\n color: z\n .string()\n .optional()\n .describe(\n \"Series color (single-series mode only). One of `emphasis`, `secondary`, `alpha-70`, `white`, `white-70`, or `white-50`, matching Text's `color` tokens; other values have no visual effect.\",\n ),\n }),\n render: ({\n variant,\n data,\n series,\n stacked,\n showAxis,\n showLegend,\n color,\n }) => {\n const usesExtendedFeatures =\n variant === \"area\" ||\n !!stacked ||\n !!showAxis ||\n !!showLegend ||\n (Array.isArray(series) && series.length > 0);\n\n if (!usesExtendedFeatures) {\n const points = Array.isArray(data) ? data : [];\n const n = points.length;\n const values = points.map((d) => clampValue(d?.value));\n const max = values.reduce((m, v) => Math.max(m, v), 0);\n const slot = n > 0 ? CHART_WIDTH / n : 0;\n const gap = slot * 0.2;\n const barWidth = slot - gap;\n\n return (\n <svg\n data-aui=\"chart\"\n data-aui-variant={variant}\n data-aui-color={color}\n role=\"img\"\n aria-label={`${variant} chart with ${n} data points`}\n viewBox={`0 0 ${CHART_WIDTH} ${CHART_HEIGHT}`}\n preserveAspectRatio=\"none\"\n >\n {variant === \"bar\" ? (\n values.map((v, i) => {\n const height = max > 0 ? (v / max) * CHART_HEIGHT : 0;\n return (\n <rect\n key={i}\n x={i * slot + gap / 2}\n y={CHART_HEIGHT - height}\n width={barWidth}\n height={height}\n fill=\"currentColor\"\n />\n );\n })\n ) : n === 1 ? (\n <circle\n cx={CHART_WIDTH / 2}\n cy={yFor(values[0] ?? 0, max)}\n r={2}\n fill=\"currentColor\"\n />\n ) : n > 1 ? (\n <polyline\n points={values\n .map(\n (v, i) => `${(i / (n - 1)) * CHART_WIDTH},${yFor(v, max)}`,\n )\n .join(\" \")}\n fill=\"none\"\n stroke=\"currentColor\"\n />\n ) : null}\n </svg>\n );\n }\n\n const { count, series: normalized } = normalizeSeries(series, data);\n const stackedScale =\n !!stacked && (variant === \"bar\" || variant === \"area\");\n const rawMax = computeMax(normalized, count, stackedScale);\n const scaleMax = showAxis ? niceMax(rawMax) : rawMax;\n const marks = renderSeriesMarks(\n variant,\n normalized,\n count,\n scaleMax,\n stackedScale,\n );\n\n const svg = (\n <svg\n data-aui=\"chart\"\n data-aui-variant={variant}\n data-aui-color={color}\n role=\"img\"\n aria-label={`${variant} chart with ${count} data points`}\n viewBox={`0 0 ${CHART_WIDTH} ${CHART_HEIGHT}`}\n preserveAspectRatio=\"none\"\n >\n {marks}\n </svg>\n );\n\n if (!showAxis && !showLegend) return svg;\n\n return (\n <div data-aui=\"chart-frame\">\n {showAxis ? (\n <div data-aui=\"chart-ticks\">\n {tickValues(scaleMax).map((tick, i) => (\n <div key={i}>{formatTick(tick)}</div>\n ))}\n </div>\n ) : null}\n {svg}\n {showAxis ? (\n <div data-aui=\"chart-xlabels\">\n {(normalized[0]?.labels ?? []).map((label, i) => (\n <div key={i}>{label ?? \"\"}</div>\n ))}\n </div>\n ) : null}\n {showLegend ? (\n <div data-aui=\"chart-legend\">\n {normalized.map((s, i) => (\n <span\n key={i}\n data-aui=\"chart-legend-item\"\n data-aui-series={i % SERIES_PALETTE_SIZE}\n >\n <span data-aui=\"chart-legend-swatch\" />\n {s.label ?? `Series ${i + 1}`}\n </span>\n ))}\n </div>\n ) : null}\n </div>\n );\n },\n },\n} satisfies GenerativeUILibrary;\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assistant-ui/react-generative-ui",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.13",
|
|
4
4
|
"description": "Generative UI components for assistant-ui",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"generative-ui",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
],
|
|
51
51
|
"sideEffects": false,
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"assistant-stream": "^0.3.
|
|
53
|
+
"assistant-stream": "^0.3.36"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
56
|
"@assistant-ui/react": "^0.15.0",
|
|
@@ -65,13 +65,13 @@
|
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
67
|
"@types/json-schema": "^7.0.15",
|
|
68
|
-
"@types/react": "^19.2.
|
|
68
|
+
"@types/react": "^19.2.18",
|
|
69
69
|
"react": "^19.2.8",
|
|
70
70
|
"react-dom": "^19.2.8",
|
|
71
71
|
"vitest": "^4.1.10",
|
|
72
72
|
"zod": "^4.4.3",
|
|
73
|
-
"@assistant-ui/react": "0.15.
|
|
74
|
-
"@assistant-ui/x-buildutils": "0.0.
|
|
73
|
+
"@assistant-ui/react": "0.15.11",
|
|
74
|
+
"@assistant-ui/x-buildutils": "0.0.22"
|
|
75
75
|
},
|
|
76
76
|
"publishConfig": {
|
|
77
77
|
"access": "public",
|