@assistant-ui/react-generative-ui 0.0.7 → 0.0.8
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/JSONGenerativeUI.client.d.ts +0 -1
- package/dist/JSONGenerativeUI.client.d.ts.map +1 -1
- package/dist/JSONGenerativeUI.server.d.ts +0 -1
- package/dist/JSONGenerativeUI.server.d.ts.map +1 -1
- package/dist/JSONGenerativeUI.shared.d.ts +0 -1
- package/dist/JSONGenerativeUI.shared.d.ts.map +1 -1
- package/dist/actionRegistry.d.ts +2 -11
- package/dist/actionRegistry.d.ts.map +1 -1
- package/dist/actionRegistry.js.map +1 -1
- package/dist/buildPresentParameters.d.ts +5 -5
- package/dist/buildPresentParameters.d.ts.map +1 -1
- package/dist/buildPresentParameters.js +10 -5
- package/dist/buildPresentParameters.js.map +1 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/defineGenerativeComponents.d.ts +0 -1
- package/dist/defineGenerativeComponents.d.ts.map +1 -1
- package/dist/generativeUIToJSX.d.ts +15 -10
- package/dist/generativeUIToJSX.d.ts.map +1 -1
- package/dist/generativeUIToJSX.js +47 -17
- package/dist/generativeUIToJSX.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.js +2 -2
- package/dist/index.server.d.ts +4 -3
- package/dist/index.server.js +2 -2
- package/dist/index.shared.d.ts +3 -3
- package/dist/index.shared.js +2 -2
- package/dist/ir.d.ts +6 -6
- package/dist/ir.d.ts.map +1 -1
- package/dist/ir.js +27 -1
- package/dist/ir.js.map +1 -1
- package/dist/node_modules/.pnpm/@types_json-schema@7.0.15/node_modules/@types/json-schema/index.d.ts +3 -3
- package/dist/node_modules/.pnpm/@types_json-schema@7.0.15/node_modules/@types/json-schema/index.d.ts.map +1 -1
- package/dist/renderGenerativeUI.d.ts +0 -1
- package/dist/renderGenerativeUI.d.ts.map +1 -1
- package/dist/slack/constants.d.ts +111 -0
- package/dist/slack/constants.d.ts.map +1 -0
- package/dist/slack/constants.js +168 -0
- package/dist/slack/constants.js.map +1 -0
- package/dist/slack/decodeBlockAction.d.ts +11 -0
- package/dist/slack/decodeBlockAction.d.ts.map +1 -0
- package/dist/slack/decodeBlockAction.js +40 -0
- package/dist/slack/decodeBlockAction.js.map +1 -0
- package/dist/slack/fromSlackBlocks.d.ts +30 -0
- package/dist/slack/fromSlackBlocks.d.ts.map +1 -0
- package/dist/slack/fromSlackBlocks.js +447 -0
- package/dist/slack/fromSlackBlocks.js.map +1 -0
- package/dist/slack/toSlackBlocks.d.ts +7 -0
- package/dist/slack/toSlackBlocks.d.ts.map +1 -0
- package/dist/slack/toSlackBlocks.js +649 -0
- package/dist/slack/toSlackBlocks.js.map +1 -0
- package/dist/slack/types.d.ts +178 -0
- package/dist/slack/types.d.ts.map +1 -0
- package/dist/slack/types.js +0 -0
- package/dist/slack.d.ts +5 -0
- package/dist/slack.js +4 -0
- package/dist/teams/boundSpec.d.ts +20 -0
- package/dist/teams/boundSpec.d.ts.map +1 -0
- package/dist/teams/boundSpec.js +67 -0
- package/dist/teams/boundSpec.js.map +1 -0
- package/dist/teams/constants.d.ts +57 -0
- package/dist/teams/constants.d.ts.map +1 -0
- package/dist/teams/constants.js +92 -0
- package/dist/teams/constants.js.map +1 -0
- package/dist/teams/decodeSubmitData.d.ts +25 -0
- package/dist/teams/decodeSubmitData.d.ts.map +1 -0
- package/dist/teams/decodeSubmitData.js +44 -0
- package/dist/teams/decodeSubmitData.js.map +1 -0
- package/dist/teams/toAdaptiveCard.d.ts +39 -0
- package/dist/teams/toAdaptiveCard.d.ts.map +1 -0
- package/dist/teams/toAdaptiveCard.js +496 -0
- package/dist/teams/toAdaptiveCard.js.map +1 -0
- package/dist/teams/toTeamsAttachments.d.ts +13 -0
- package/dist/teams/toTeamsAttachments.d.ts.map +1 -0
- package/dist/teams/toTeamsAttachments.js +95 -0
- package/dist/teams/toTeamsAttachments.js.map +1 -0
- package/dist/teams/types.d.ts +196 -0
- package/dist/teams/types.d.ts.map +1 -0
- package/dist/teams/types.js +0 -0
- package/dist/teams.d.ts +5 -0
- package/dist/teams.js +4 -0
- package/dist/types.d.ts +7 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/vocabulary/alert.d.ts +6 -12
- package/dist/vocabulary/alert.d.ts.map +1 -1
- package/dist/vocabulary/alert.js +13 -4
- package/dist/vocabulary/alert.js.map +1 -1
- package/dist/vocabulary/collectFormValues.d.ts +23 -0
- package/dist/vocabulary/collectFormValues.d.ts.map +1 -0
- package/dist/vocabulary/collectFormValues.js +32 -0
- package/dist/vocabulary/collectFormValues.js.map +1 -0
- package/dist/vocabulary/data.d.ts +16 -17
- package/dist/vocabulary/data.d.ts.map +1 -1
- package/dist/vocabulary/data.js +244 -13
- package/dist/vocabulary/data.js.map +1 -1
- package/dist/vocabulary/dispatch.d.ts +11 -0
- package/dist/vocabulary/dispatch.d.ts.map +1 -0
- package/dist/vocabulary/dispatch.js +21 -0
- package/dist/vocabulary/dispatch.js.map +1 -0
- package/dist/vocabulary/fact.d.ts +1 -6
- package/dist/vocabulary/fact.d.ts.map +1 -1
- package/dist/vocabulary/form.d.ts +14 -0
- package/dist/vocabulary/form.d.ts.map +1 -0
- package/dist/vocabulary/form.js +23 -0
- package/dist/vocabulary/form.js.map +1 -0
- package/dist/vocabulary/icon.d.ts +44 -0
- package/dist/vocabulary/icon.d.ts.map +1 -0
- package/dist/vocabulary/icon.js +272 -0
- package/dist/vocabulary/icon.js.map +1 -0
- package/dist/vocabulary/index.d.ts +0 -1
- package/dist/vocabulary/index.d.ts.map +1 -1
- package/dist/vocabulary/index.js +7 -1
- package/dist/vocabulary/index.js.map +1 -1
- package/dist/vocabulary/interactive.d.ts +45 -33
- package/dist/vocabulary/interactive.d.ts.map +1 -1
- package/dist/vocabulary/interactive.js +78 -29
- package/dist/vocabulary/interactive.js.map +1 -1
- package/dist/vocabulary/layout.d.ts +27 -23
- package/dist/vocabulary/layout.d.ts.map +1 -1
- package/dist/vocabulary/layout.js +78 -14
- package/dist/vocabulary/layout.js.map +1 -1
- package/dist/vocabulary/list.d.ts +17 -0
- package/dist/vocabulary/list.d.ts.map +1 -0
- package/dist/vocabulary/list.js +48 -0
- package/dist/vocabulary/list.js.map +1 -0
- package/dist/vocabulary/media.d.ts +3 -9
- package/dist/vocabulary/media.d.ts.map +1 -1
- package/dist/vocabulary/media.js +17 -7
- package/dist/vocabulary/media.js.map +1 -1
- package/dist/vocabulary/text.d.ts +3 -17
- package/dist/vocabulary/text.d.ts.map +1 -1
- package/package.json +13 -5
- package/src/actionRegistry.ts +2 -10
- package/src/buildPresentParameters.ts +15 -9
- package/src/generativeUIToJSX.test.ts +186 -0
- package/src/generativeUIToJSX.ts +112 -21
- package/src/index.shared.ts +3 -0
- package/src/ir.ts +31 -5
- package/src/renderGenerativeUI.test.tsx +16 -2
- package/src/slack/constants.ts +217 -0
- package/src/slack/decodeBlockAction.test.ts +135 -0
- package/src/slack/decodeBlockAction.ts +66 -0
- package/src/slack/fromSlackBlocks.test.ts +1145 -0
- package/src/slack/fromSlackBlocks.ts +709 -0
- package/src/slack/toSlackBlocks.test.ts +1936 -0
- package/src/slack/toSlackBlocks.ts +1319 -0
- package/src/slack/types.ts +230 -0
- package/src/slack.ts +37 -0
- package/src/teams/boundSpec.ts +102 -0
- package/src/teams/constants.ts +127 -0
- package/src/teams/decodeSubmitData.test.ts +148 -0
- package/src/teams/decodeSubmitData.ts +57 -0
- package/src/teams/toAdaptiveCard.test.ts +1410 -0
- package/src/teams/toAdaptiveCard.ts +762 -0
- package/src/teams/toTeamsAttachments.test.ts +29 -0
- package/src/teams/toTeamsAttachments.ts +137 -0
- package/src/teams/types.ts +237 -0
- package/src/teams.ts +34 -0
- package/src/vocabulary/alert.test.tsx +72 -2
- package/src/vocabulary/alert.tsx +24 -5
- package/src/vocabulary/collectFormValues.test.ts +127 -0
- package/src/vocabulary/collectFormValues.ts +58 -0
- package/src/vocabulary/data.test.tsx +503 -3
- package/src/vocabulary/data.tsx +426 -18
- package/src/vocabulary/dispatch.ts +22 -0
- package/src/vocabulary/form.test.tsx +112 -0
- package/src/vocabulary/form.tsx +34 -0
- package/src/vocabulary/icon.test.tsx +64 -0
- package/src/vocabulary/icon.tsx +174 -0
- package/src/vocabulary/index.test.ts +7 -0
- package/src/vocabulary/index.ts +6 -0
- package/src/vocabulary/interactive.dispatch.test.tsx +243 -1
- package/src/vocabulary/interactive.test.tsx +143 -3
- package/src/vocabulary/interactive.tsx +134 -30
- package/src/vocabulary/layout.test.tsx +231 -3
- package/src/vocabulary/layout.tsx +168 -15
- package/src/vocabulary/list.test.tsx +195 -0
- package/src/vocabulary/list.tsx +48 -0
- package/src/vocabulary/media.test.tsx +36 -4
- package/src/vocabulary/media.tsx +27 -8
|
@@ -0,0 +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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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { TeamsAttachmentsResult, ToAdaptiveCardOptions } from "./types.js";
|
|
2
|
+
//#region src/teams/toTeamsAttachments.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Converts a generative-ui tree into Teams bot-framework message attachments.
|
|
5
|
+
* A root-level `Carousel` becomes one attachment per `Card` child (clamped to
|
|
6
|
+
* {@link CAROUSEL_ATTACHMENT_CAP}) with `attachmentLayout: "carousel"`;
|
|
7
|
+
* anything else converts through {@link toAdaptiveCard}'s engine into a
|
|
8
|
+
* single attachment. Never throws.
|
|
9
|
+
*/
|
|
10
|
+
declare function toTeamsAttachments(node: unknown, _options?: ToAdaptiveCardOptions): TeamsAttachmentsResult;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { toTeamsAttachments };
|
|
13
|
+
//# sourceMappingURL=toTeamsAttachments.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toTeamsAttachments.d.ts","names":[],"sources":["../../src/teams/toTeamsAttachments.ts"],"mappings":";;;;;;;;;iBAiFgB,mBACd,eACA,WAAW,wBACV"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { normalizeSpec } from "../ir.js";
|
|
2
|
+
import { buildAttachment, clampReasonDetail, utf8ByteLength } from "./constants.js";
|
|
3
|
+
import { boundSpec } from "./boundSpec.js";
|
|
4
|
+
import { convertElement, convertRootToCard } from "./toAdaptiveCard.js";
|
|
5
|
+
//#region src/teams/toTeamsAttachments.ts
|
|
6
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7
|
+
const isElement = (node) => isRecord(node);
|
|
8
|
+
const normalizedList = (node) => {
|
|
9
|
+
if (node === void 0 || node === null) return [];
|
|
10
|
+
if (!Array.isArray(node)) return [node];
|
|
11
|
+
return node.flatMap((child) => normalizedList(child));
|
|
12
|
+
};
|
|
13
|
+
const clampArray = (value, cap) => ({
|
|
14
|
+
items: value.slice(0, cap),
|
|
15
|
+
truncated: value.length > cap
|
|
16
|
+
});
|
|
17
|
+
/**
|
|
18
|
+
* Recognizes a root that is a single `Carousel` element, returning its `Card`
|
|
19
|
+
* children. Every child is routed through {@link convertElement} first (its
|
|
20
|
+
* result is discarded) so an unknown or unsupported child still produces its
|
|
21
|
+
* usual "dropped" warning instead of being filtered out silently; non-card
|
|
22
|
+
* children are then dropped. `undefined` means the root is not exclusively a
|
|
23
|
+
* root-level carousel, so the caller falls back to converting the whole tree
|
|
24
|
+
* as one card.
|
|
25
|
+
*/
|
|
26
|
+
function rootCarouselCards(root, context) {
|
|
27
|
+
const element = Array.isArray(root) && root.length === 1 ? root[0] : root;
|
|
28
|
+
if (!isElement(element) || element.type !== "Carousel") return void 0;
|
|
29
|
+
const cards = [];
|
|
30
|
+
for (const child of normalizedList(element.children)) {
|
|
31
|
+
if (!isElement(child)) continue;
|
|
32
|
+
if (child.type === "Card") cards.push(child);
|
|
33
|
+
else convertElement(child, context, 1);
|
|
34
|
+
}
|
|
35
|
+
return cards;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Converts a generative-ui tree into Teams bot-framework message attachments.
|
|
39
|
+
* A root-level `Carousel` becomes one attachment per `Card` child (clamped to
|
|
40
|
+
* {@link CAROUSEL_ATTACHMENT_CAP}) with `attachmentLayout: "carousel"`;
|
|
41
|
+
* anything else converts through {@link toAdaptiveCard}'s engine into a
|
|
42
|
+
* single attachment. Never throws.
|
|
43
|
+
*/
|
|
44
|
+
function toTeamsAttachments(node, _options) {
|
|
45
|
+
const warnings = [];
|
|
46
|
+
const context = {
|
|
47
|
+
warnings,
|
|
48
|
+
usedInputIds: /* @__PURE__ */ new Set()
|
|
49
|
+
};
|
|
50
|
+
try {
|
|
51
|
+
const { root } = normalizeSpec(boundSpec(node, (reason) => warnings.push({
|
|
52
|
+
code: "clamped",
|
|
53
|
+
component: "Root",
|
|
54
|
+
detail: clampReasonDetail(reason)
|
|
55
|
+
})));
|
|
56
|
+
const carouselCards = rootCarouselCards(root, context);
|
|
57
|
+
if (carouselCards !== void 0) {
|
|
58
|
+
const { items, truncated } = clampArray(carouselCards, 10);
|
|
59
|
+
if (truncated) warnings.push({
|
|
60
|
+
code: "clamped",
|
|
61
|
+
component: "Carousel",
|
|
62
|
+
detail: `cards were clamped to 10 entries.`
|
|
63
|
+
});
|
|
64
|
+
const attachments = items.map((child) => buildAttachment(convertRootToCard(child, context)));
|
|
65
|
+
const size = utf8ByteLength(JSON.stringify(attachments));
|
|
66
|
+
if (size > 8e4) warnings.push({
|
|
67
|
+
code: "clamped",
|
|
68
|
+
component: "Carousel",
|
|
69
|
+
detail: `the carousel attachments total ${size} bytes, exceeding Teams' 100 KB bot message limit.`
|
|
70
|
+
});
|
|
71
|
+
return {
|
|
72
|
+
attachments,
|
|
73
|
+
attachmentLayout: "carousel",
|
|
74
|
+
warnings
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
attachments: [buildAttachment(convertRootToCard(root, context))],
|
|
79
|
+
warnings
|
|
80
|
+
};
|
|
81
|
+
} catch {
|
|
82
|
+
return {
|
|
83
|
+
attachments: [],
|
|
84
|
+
warnings: [{
|
|
85
|
+
code: "dropped",
|
|
86
|
+
component: "Root",
|
|
87
|
+
detail: "Input could not be converted."
|
|
88
|
+
}]
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
//#endregion
|
|
93
|
+
export { toTeamsAttachments };
|
|
94
|
+
|
|
95
|
+
//# sourceMappingURL=toTeamsAttachments.js.map
|
|
@@ -0,0 +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;EAQF,MAAM,EAAE,SAAS,cAPD,UAAU,OAAO,WAC/B,SAAS,KAAK;GACZ,MAAM;GACN,WAAW;GACX,QAAQ,kBAAkB,MAAM;EAClC,CAAC,CAEkC,CAAU;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"}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
//#region src/teams/types.d.ts
|
|
2
|
+
/** A TextBlock size, from Adaptive Card's closed size enum. */
|
|
3
|
+
type TeamsTextSize = "small" | "medium" | "large" | "extraLarge";
|
|
4
|
+
/** A Container's style, driving its accent color in Teams' renderer. */
|
|
5
|
+
type TeamsContainerStyle = "default" | "emphasis" | "good" | "attention" | "warning" | "accent";
|
|
6
|
+
/** A run of wrapped text. Every TextBlock we emit sets `wrap: true`. */
|
|
7
|
+
interface TeamsTextBlock {
|
|
8
|
+
readonly type: "TextBlock";
|
|
9
|
+
readonly text: string;
|
|
10
|
+
readonly wrap: true;
|
|
11
|
+
readonly size?: TeamsTextSize;
|
|
12
|
+
readonly weight?: "bolder";
|
|
13
|
+
readonly style?: "heading";
|
|
14
|
+
readonly isSubtle?: true;
|
|
15
|
+
readonly separator?: true;
|
|
16
|
+
readonly spacing?: "large";
|
|
17
|
+
}
|
|
18
|
+
/** An image element. */
|
|
19
|
+
interface TeamsImage {
|
|
20
|
+
readonly type: "Image";
|
|
21
|
+
readonly url: string;
|
|
22
|
+
readonly altText?: string;
|
|
23
|
+
readonly size?: "small" | "medium" | "large";
|
|
24
|
+
readonly separator?: true;
|
|
25
|
+
readonly spacing?: "large";
|
|
26
|
+
}
|
|
27
|
+
/** One label/value entry inside a {@link TeamsFactSet}. */
|
|
28
|
+
interface TeamsFact {
|
|
29
|
+
readonly title: string;
|
|
30
|
+
readonly value: string;
|
|
31
|
+
}
|
|
32
|
+
/** A set of label/value facts, rendered as an aligned two-column list. */
|
|
33
|
+
interface TeamsFactSet {
|
|
34
|
+
readonly type: "FactSet";
|
|
35
|
+
readonly facts: readonly TeamsFact[];
|
|
36
|
+
readonly separator?: true;
|
|
37
|
+
readonly spacing?: "large";
|
|
38
|
+
}
|
|
39
|
+
/** The `aui` envelope nested inside every {@link TeamsSubmitAction}'s `data`. */
|
|
40
|
+
interface TeamsSubmitData {
|
|
41
|
+
readonly aui: {
|
|
42
|
+
readonly type: string;
|
|
43
|
+
readonly payload?: Record<string, unknown>;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* A submit action. `data` carries the resume payload under an `aui` key so
|
|
48
|
+
* {@link decodeSubmitData} can invert it out of the merged `activity.value`
|
|
49
|
+
* a Teams bot receives, alongside whatever other named inputs shared the card.
|
|
50
|
+
*/
|
|
51
|
+
interface TeamsSubmitAction {
|
|
52
|
+
readonly type: "Action.Submit";
|
|
53
|
+
readonly title: string;
|
|
54
|
+
readonly data: TeamsSubmitData;
|
|
55
|
+
readonly mode?: "secondary";
|
|
56
|
+
}
|
|
57
|
+
/** The action shape accepted at the card's top-level `actions` field. */
|
|
58
|
+
type TeamsCardAction = TeamsSubmitAction;
|
|
59
|
+
/** A bar of submit actions, rendered inline in the body flow. */
|
|
60
|
+
interface TeamsActionSet {
|
|
61
|
+
readonly type: "ActionSet";
|
|
62
|
+
readonly actions: readonly TeamsSubmitAction[];
|
|
63
|
+
readonly separator?: true;
|
|
64
|
+
readonly spacing?: "large";
|
|
65
|
+
}
|
|
66
|
+
/** A bordered grouping container. */
|
|
67
|
+
interface TeamsContainer {
|
|
68
|
+
readonly type: "Container";
|
|
69
|
+
readonly style?: TeamsContainerStyle;
|
|
70
|
+
readonly items: readonly TeamsCardElement[];
|
|
71
|
+
readonly selectAction?: TeamsSubmitAction;
|
|
72
|
+
readonly separator?: true;
|
|
73
|
+
readonly spacing?: "large";
|
|
74
|
+
}
|
|
75
|
+
/** One column inside a {@link TeamsColumnSet}. */
|
|
76
|
+
interface TeamsColumn {
|
|
77
|
+
readonly type: "Column";
|
|
78
|
+
readonly width: "auto";
|
|
79
|
+
readonly items: readonly TeamsCardElement[];
|
|
80
|
+
}
|
|
81
|
+
/** A horizontal row of columns. */
|
|
82
|
+
interface TeamsColumnSet {
|
|
83
|
+
readonly type: "ColumnSet";
|
|
84
|
+
readonly columns: readonly TeamsColumn[];
|
|
85
|
+
readonly separator?: true;
|
|
86
|
+
readonly spacing?: "large";
|
|
87
|
+
}
|
|
88
|
+
/** One selectable choice inside a {@link TeamsInputChoiceSet}. */
|
|
89
|
+
interface TeamsInputChoice {
|
|
90
|
+
readonly title: string;
|
|
91
|
+
readonly value: string;
|
|
92
|
+
}
|
|
93
|
+
/** A dropdown (`compact`) or radio group (`expanded`) input. */
|
|
94
|
+
interface TeamsInputChoiceSet {
|
|
95
|
+
readonly type: "Input.ChoiceSet";
|
|
96
|
+
readonly id: string;
|
|
97
|
+
readonly style: "compact" | "expanded";
|
|
98
|
+
readonly choices: readonly TeamsInputChoice[];
|
|
99
|
+
readonly placeholder?: string;
|
|
100
|
+
readonly label?: string;
|
|
101
|
+
readonly value?: string;
|
|
102
|
+
readonly separator?: true;
|
|
103
|
+
readonly spacing?: "large";
|
|
104
|
+
}
|
|
105
|
+
/** A checkbox-shaped boolean input. */
|
|
106
|
+
interface TeamsInputToggle {
|
|
107
|
+
readonly type: "Input.Toggle";
|
|
108
|
+
readonly id: string;
|
|
109
|
+
readonly title: string;
|
|
110
|
+
readonly value: "true" | "false";
|
|
111
|
+
readonly valueOn: "true";
|
|
112
|
+
readonly valueOff: "false";
|
|
113
|
+
readonly separator?: true;
|
|
114
|
+
readonly spacing?: "large";
|
|
115
|
+
}
|
|
116
|
+
/** A single or multi-line text input. */
|
|
117
|
+
interface TeamsInputText {
|
|
118
|
+
readonly type: "Input.Text";
|
|
119
|
+
readonly id: string;
|
|
120
|
+
readonly label?: string;
|
|
121
|
+
readonly placeholder?: string;
|
|
122
|
+
readonly isMultiline?: true;
|
|
123
|
+
readonly separator?: true;
|
|
124
|
+
readonly spacing?: "large";
|
|
125
|
+
}
|
|
126
|
+
/** A date input, restricted to `YYYY-MM-DD` values. */
|
|
127
|
+
interface TeamsInputDate {
|
|
128
|
+
readonly type: "Input.Date";
|
|
129
|
+
readonly id: string;
|
|
130
|
+
readonly label?: string;
|
|
131
|
+
readonly value?: string;
|
|
132
|
+
readonly min?: string;
|
|
133
|
+
readonly max?: string;
|
|
134
|
+
readonly separator?: true;
|
|
135
|
+
readonly spacing?: "large";
|
|
136
|
+
}
|
|
137
|
+
/** One cell inside a {@link TeamsTableRow}. */
|
|
138
|
+
interface TeamsTableCell {
|
|
139
|
+
readonly type: "TableCell";
|
|
140
|
+
readonly items: readonly TeamsTextBlock[];
|
|
141
|
+
}
|
|
142
|
+
/** One row inside a {@link TeamsTable}. */
|
|
143
|
+
interface TeamsTableRow {
|
|
144
|
+
readonly type: "TableRow";
|
|
145
|
+
readonly cells: readonly TeamsTableCell[];
|
|
146
|
+
}
|
|
147
|
+
/** A column width definition inside a {@link TeamsTable}. */
|
|
148
|
+
interface TeamsTableColumnDefinition {
|
|
149
|
+
readonly width: 1;
|
|
150
|
+
}
|
|
151
|
+
/** A native table. `rows[0]` is the header row when `firstRowAsHeaders` is true. */
|
|
152
|
+
interface TeamsTable {
|
|
153
|
+
readonly type: "Table";
|
|
154
|
+
readonly firstRowAsHeaders: boolean;
|
|
155
|
+
readonly columns: readonly TeamsTableColumnDefinition[];
|
|
156
|
+
readonly rows: readonly TeamsTableRow[];
|
|
157
|
+
readonly separator?: true;
|
|
158
|
+
readonly spacing?: "large";
|
|
159
|
+
}
|
|
160
|
+
/** A body element emitted by {@link toAdaptiveCard}. */
|
|
161
|
+
type TeamsCardElement = TeamsTextBlock | TeamsImage | TeamsFactSet | TeamsActionSet | TeamsContainer | TeamsColumnSet | TeamsInputChoiceSet | TeamsInputToggle | TeamsInputText | TeamsInputDate | TeamsTable;
|
|
162
|
+
/** A Microsoft Teams Adaptive Card, pinned to schema version 1.5 (the Teams desktop/web cap; mobile clients cap at 1.2). */
|
|
163
|
+
interface TeamsAdaptiveCard {
|
|
164
|
+
readonly $schema: "http://adaptivecards.io/schemas/adaptive-card.json";
|
|
165
|
+
readonly type: "AdaptiveCard";
|
|
166
|
+
readonly version: "1.5";
|
|
167
|
+
readonly body: readonly TeamsCardElement[];
|
|
168
|
+
readonly actions: readonly TeamsCardAction[];
|
|
169
|
+
}
|
|
170
|
+
/** An Adaptive Card wrapped as a bot-framework message attachment. */
|
|
171
|
+
interface TeamsCardAttachment {
|
|
172
|
+
readonly contentType: "application/vnd.microsoft.card.adaptive";
|
|
173
|
+
readonly content: TeamsAdaptiveCard;
|
|
174
|
+
}
|
|
175
|
+
/** A non-fatal downgrade reported during Teams conversion. */
|
|
176
|
+
interface TeamsConversionWarning {
|
|
177
|
+
readonly code: "clamped" | "dropped" | "fallback";
|
|
178
|
+
readonly component: string;
|
|
179
|
+
readonly detail: string;
|
|
180
|
+
}
|
|
181
|
+
/** Options accepted by {@link toAdaptiveCard} and {@link toTeamsAttachments}. Reserved for future extension; no options are currently defined. */
|
|
182
|
+
interface ToAdaptiveCardOptions {}
|
|
183
|
+
/** The card and non-fatal warnings produced by {@link toAdaptiveCard}. */
|
|
184
|
+
interface AdaptiveCardResult {
|
|
185
|
+
readonly card: TeamsAdaptiveCard;
|
|
186
|
+
readonly warnings: TeamsConversionWarning[];
|
|
187
|
+
}
|
|
188
|
+
/** The attachments and non-fatal warnings produced by {@link toTeamsAttachments}. */
|
|
189
|
+
interface TeamsAttachmentsResult {
|
|
190
|
+
readonly attachments: TeamsCardAttachment[];
|
|
191
|
+
readonly attachmentLayout?: "carousel";
|
|
192
|
+
readonly warnings: TeamsConversionWarning[];
|
|
193
|
+
}
|
|
194
|
+
//#endregion
|
|
195
|
+
export { AdaptiveCardResult, TeamsActionSet, TeamsAdaptiveCard, TeamsAttachmentsResult, TeamsCardAction, TeamsCardAttachment, TeamsCardElement, TeamsColumn, TeamsColumnSet, TeamsContainer, TeamsContainerStyle, TeamsConversionWarning, TeamsFact, TeamsFactSet, TeamsImage, TeamsInputChoice, TeamsInputChoiceSet, TeamsInputDate, TeamsInputText, TeamsInputToggle, TeamsSubmitAction, TeamsSubmitData, TeamsTable, TeamsTableCell, TeamsTableColumnDefinition, TeamsTableRow, TeamsTextBlock, TeamsTextSize, ToAdaptiveCardOptions };
|
|
196
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","names":[],"sources":["../../src/teams/types.ts"],"mappings":";;KACY;;KAGA;;UASK;WACN;WACA;WACA;WACA,OAAO;WACP;WACA;WACA;WACA;WACA;;;UAIM;WACN;WACA;WACA;WACA;WACA;WACA;;;UAIM;WACN;WACA;;;UAIM;WACN;WACA,gBAAgB;WAChB;WACA;;;UAIM;WACN;aACE;aACA,UAAU;;;;;;;;UASN;WACN;WACA;WACA,MAAM;WACN;;;KAIC,kBAAkB;;UAGb;WACN;WACA,kBAAkB;WAClB;WACA;;;UAIM;WACN;WACA,QAAQ;WACR,gBAAgB;WAChB,eAAe;WACf;WACA;;;UAIM;WACN;WACA;WACA,gBAAgB;;;UAIV;WACN;WACA,kBAAkB;WAClB;WACA;;;UAIM;WACN;WACA;;;UAIM;WACN;WACA;WACA;WACA,kBAAkB;WAClB;WACA;WACA;WACA;WACA;;;UAIM;WACN;WACA;WACA;WACA;WACA;WACA;WACA;WACA;;;UAIM;WACN;WACA;WACA;WACA;WACA;WACA;WACA;;;UAIM;WACN;WACA;WACA;WACA;WACA;WACA;WACA;WACA;;;UAIM;WACN;WACA,gBAAgB;;;UAIV;WACN;WACA,gBAAgB;;;UAIV;WACN;;;UAIM;WACN;WACA;WACA,kBAAkB;WAClB,eAAe;WACf;WACA;;;KAIC,mBACR,iBACA,aACA,eACA,iBACA,iBACA,iBACA,sBACA,mBACA,iBACA,iBACA;;UAGa;WACN;WACA;WACA;WACA,eAAe;WACf,kBAAkB;;;UAIZ;WACN;WACA,SAAS;;;UAIH;WACN;WACA;WACA;;;UAIM;;UAGA;WACN,MAAM;WACN,UAAU;;;UAIJ;WACN,aAAa;WACb;WACA,UAAU"}
|
|
File without changes
|
package/dist/teams.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { decodeSubmitData } from "./teams/decodeSubmitData.js";
|
|
2
|
+
import { AdaptiveCardResult, TeamsActionSet, TeamsAdaptiveCard, TeamsAttachmentsResult, TeamsCardAction, TeamsCardAttachment, TeamsCardElement, TeamsColumn, TeamsColumnSet, TeamsContainer, TeamsContainerStyle, TeamsConversionWarning, TeamsFact, TeamsFactSet, TeamsImage, TeamsInputChoice, TeamsInputChoiceSet, TeamsInputDate, TeamsInputText, TeamsInputToggle, TeamsSubmitAction, TeamsSubmitData, TeamsTable, TeamsTableCell, TeamsTableColumnDefinition, TeamsTableRow, TeamsTextBlock, TeamsTextSize, ToAdaptiveCardOptions } from "./teams/types.js";
|
|
3
|
+
import { toAdaptiveCard } from "./teams/toAdaptiveCard.js";
|
|
4
|
+
import { toTeamsAttachments } from "./teams/toTeamsAttachments.js";
|
|
5
|
+
export { type AdaptiveCardResult, type TeamsActionSet, type TeamsAdaptiveCard, type TeamsAttachmentsResult, type TeamsCardAction, type TeamsCardAttachment, type TeamsCardElement, type TeamsColumn, type TeamsColumnSet, type TeamsContainer, type TeamsContainerStyle, type TeamsConversionWarning, type TeamsFact, type TeamsFactSet, type TeamsImage, type TeamsInputChoice, type TeamsInputChoiceSet, type TeamsInputDate, type TeamsInputText, type TeamsInputToggle, type TeamsSubmitAction, type TeamsSubmitData, type TeamsTable, type TeamsTableCell, type TeamsTableColumnDefinition, type TeamsTableRow, type TeamsTextBlock, type TeamsTextSize, type ToAdaptiveCardOptions, decodeSubmitData, toAdaptiveCard, toTeamsAttachments };
|
package/dist/teams.js
ADDED
package/dist/types.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Action, NormalizedUIElement } from "./ir.js";
|
|
2
2
|
import { ReactNode } from "react";
|
|
3
3
|
import { ZodType } from "zod";
|
|
4
|
-
|
|
5
4
|
//#region src/types.d.ts
|
|
6
5
|
/**
|
|
7
6
|
* The canonical generative-ui element, normalized from the flat `$type` shape
|
|
@@ -26,7 +25,8 @@ type GenerativeUIStatus = "streaming" | "done";
|
|
|
26
25
|
type GenerativeUIDispatch = (action: Action) => unknown;
|
|
27
26
|
/** The render context threaded through {@link renderGenerativeUI}. */
|
|
28
27
|
type GenerativeUIRenderContext = {
|
|
29
|
-
/** Whether the tool call's arguments are still streaming or are complete. */
|
|
28
|
+
/** Whether the tool call's arguments are still streaming or are complete. */
|
|
29
|
+
status: GenerativeUIStatus;
|
|
30
30
|
dispatch?: GenerativeUIDispatch;
|
|
31
31
|
};
|
|
32
32
|
/**
|
|
@@ -69,7 +69,9 @@ type StaticRenderProps<P> = P & {
|
|
|
69
69
|
* `type` across renders for hook identity to be stable.
|
|
70
70
|
*/
|
|
71
71
|
type GenerativeUIComponent<P = any> = {
|
|
72
|
-
/** Natural-language description shown to the model when selecting the component. */
|
|
72
|
+
/** Natural-language description shown to the model when selecting the component. */
|
|
73
|
+
description: string;
|
|
74
|
+
/** Schema for the props the model must provide. Drives the tool parameters. */
|
|
73
75
|
properties: ZodType<P>;
|
|
74
76
|
/**
|
|
75
77
|
* Render from partially-streamed props. Widened to `boolean | undefined`
|
|
@@ -81,7 +83,8 @@ type GenerativeUIComponent<P = any> = {
|
|
|
81
83
|
render: (props: StreamingRenderProps<P>) => ReactNode;
|
|
82
84
|
} | {
|
|
83
85
|
description: string;
|
|
84
|
-
properties: ZodType<P>;
|
|
86
|
+
properties: ZodType<P>;
|
|
87
|
+
/** Render only once props are complete (the default). */
|
|
85
88
|
streamProperties?: false | undefined;
|
|
86
89
|
render: (props: StaticRenderProps<P>) => ReactNode;
|
|
87
90
|
};
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","names":[],"sources":["../src/types.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.d.ts","names":[],"sources":["../src/types.ts"],"mappings":";;;;;;;;;;;KAWY,sBAAsB;KAEtB,oBAAoB;;;;;;;KAQpB,mBACR,qEAMA;KAEQ,qBAAqB;;KAGrB;;KAGA,wBAAwB,QAAQ;;KAGhC;;EAEV,QAAQ;EACR,WAAW;;;;;;;;;;;KAYR,qBAAqB,MACrB,QAAQ;EACP,WAAW;EACX;EACA,UAAU;EACV,YAAY;MAEb;EACC,WAAW;EACX;EACA,UAAU;EACV,YAAY;;;KAIb,kBAAkB,KAAK;EAC1B,WAAW;EACX;EACA,UAAU;EACV,YAAY;;;;;;;;;;;;;;KAeF,sBAAsB;;EAG5B;;EAEA,YAAY,QAAQ;;;;;;;EAOpB;EACA,SAAS,OAAO,qBAAqB,OAAO;;EAG5C;EACA,YAAY,QAAQ;;EAEpB;EACA,SAAS,OAAO,kBAAkB,OAAO;;;;;;;;;KAUnC,sBAAsB,eAAe"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
|
|
3
2
|
//#region src/vocabulary/alert.d.ts
|
|
4
3
|
declare const alertVocabulary: {
|
|
5
4
|
Alert: {
|
|
@@ -9,24 +8,19 @@ declare const alertVocabulary: {
|
|
|
9
8
|
description: z.ZodOptional<z.ZodString>;
|
|
10
9
|
tone: z.ZodOptional<z.ZodEnum<{
|
|
11
10
|
success: "success";
|
|
11
|
+
danger: "danger";
|
|
12
12
|
info: "info";
|
|
13
13
|
warning: "warning";
|
|
14
|
-
danger: "danger";
|
|
15
14
|
}>>;
|
|
16
15
|
}, z.core.$strip>;
|
|
17
|
-
render: ({
|
|
18
|
-
title,
|
|
19
|
-
description,
|
|
20
|
-
tone,
|
|
21
|
-
children
|
|
22
|
-
}: any) => import("react").JSX.Element;
|
|
16
|
+
render: ({ title, description, tone, children }: any) => import("react").JSX.Element;
|
|
23
17
|
};
|
|
24
18
|
Carousel: {
|
|
25
19
|
description: string;
|
|
26
|
-
properties: z.ZodObject<{
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}: any) => import("react").JSX.Element;
|
|
20
|
+
properties: z.ZodObject<{
|
|
21
|
+
label: z.ZodOptional<z.ZodString>;
|
|
22
|
+
}, z.core.$strip>;
|
|
23
|
+
render: ({ label, children }: any) => import("react").JSX.Element;
|
|
30
24
|
};
|
|
31
25
|
};
|
|
32
26
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"alert.d.ts","names":[],"sources":["../../src/vocabulary/alert.tsx"],"mappings":"
|
|
1
|
+
{"version":3,"file":"alert.d.ts","names":[],"sources":["../../src/vocabulary/alert.tsx"],"mappings":";;cAOa"}
|
package/dist/vocabulary/alert.js
CHANGED
|
@@ -30,13 +30,22 @@ const alertVocabulary = {
|
|
|
30
30
|
})
|
|
31
31
|
},
|
|
32
32
|
Carousel: {
|
|
33
|
-
description: "A horizontally scrollable group of `Card` children (max 10).",
|
|
34
|
-
properties: z.object({}),
|
|
35
|
-
render: ({ children }) => {
|
|
33
|
+
description: "A horizontally scrollable group of `Card` children (max 10). Set `label` for an accessible name.",
|
|
34
|
+
properties: z.object({ label: z.string().optional().describe("Accessible label for the carousel.") }),
|
|
35
|
+
render: ({ label, children }) => {
|
|
36
|
+
const cards = Children.toArray(children).slice(0, MAX_CARDS);
|
|
37
|
+
const n = cards.length;
|
|
36
38
|
return /* @__PURE__ */ jsx("div", {
|
|
37
39
|
"data-aui": "carousel",
|
|
38
|
-
|
|
40
|
+
role: label ? "region" : "group",
|
|
41
|
+
"aria-roledescription": "carousel",
|
|
42
|
+
"aria-label": label,
|
|
43
|
+
tabIndex: 0,
|
|
44
|
+
children: cards.map((card, i) => /* @__PURE__ */ jsx("div", {
|
|
39
45
|
"data-aui": "carousel-slide",
|
|
46
|
+
role: "group",
|
|
47
|
+
"aria-roledescription": "slide",
|
|
48
|
+
"aria-label": `${i + 1} of ${n}`,
|
|
40
49
|
children: card
|
|
41
50
|
}, i))
|
|
42
51
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"alert.js","names":[],"sources":["../../src/vocabulary/alert.tsx"],"sourcesContent":["import { Children } from \"react\";\nimport { z } from \"zod\";\nimport type { GenerativeUILibrary } from \"../types\";\nimport { ALERT_TONES } from \"../ir\";\n\nconst MAX_CARDS = 10;\n\nexport const alertVocabulary = {\n Alert: {\n description:\n \"A highlighted message conveying urgency. `tone` drives the severity.\",\n properties: z.object({\n title: z.string().optional().describe(\"Alert title.\"),\n description: z\n .string()\n .optional()\n .describe(\"Supporting description text.\"),\n tone: z\n .enum(ALERT_TONES)\n .optional()\n .describe(\"Severity tone; defaults to `info`.\"),\n }),\n render: ({ title, description, tone, children }) => (\n <div data-aui=\"alert\" data-aui-tone={tone ?? \"info\"} role=\"alert\">\n {title ? <header data-aui=\"alert-title\">{title}</header> : null}\n {description ? <p data-aui=\"alert-desc\">{description}</p> : null}\n {children}\n </div>\n ),\n },\n Carousel: {\n description
|
|
1
|
+
{"version":3,"file":"alert.js","names":[],"sources":["../../src/vocabulary/alert.tsx"],"sourcesContent":["import { Children } from \"react\";\nimport { z } from \"zod\";\nimport type { GenerativeUILibrary } from \"../types\";\nimport { ALERT_TONES } from \"../ir\";\n\nconst MAX_CARDS = 10;\n\nexport const alertVocabulary = {\n Alert: {\n description:\n \"A highlighted message conveying urgency. `tone` drives the severity.\",\n properties: z.object({\n title: z.string().optional().describe(\"Alert title.\"),\n description: z\n .string()\n .optional()\n .describe(\"Supporting description text.\"),\n tone: z\n .enum(ALERT_TONES)\n .optional()\n .describe(\"Severity tone; defaults to `info`.\"),\n }),\n render: ({ title, description, tone, children }) => (\n <div data-aui=\"alert\" data-aui-tone={tone ?? \"info\"} role=\"alert\">\n {title ? <header data-aui=\"alert-title\">{title}</header> : null}\n {description ? <p data-aui=\"alert-desc\">{description}</p> : null}\n {children}\n </div>\n ),\n },\n Carousel: {\n description:\n \"A horizontally scrollable group of `Card` children (max 10). Set `label` for an accessible name.\",\n properties: z.object({\n label: z\n .string()\n .optional()\n .describe(\"Accessible label for the carousel.\"),\n }),\n render: ({ label, children }) => {\n const cards = Children.toArray(children).slice(0, MAX_CARDS);\n const n = cards.length;\n return (\n <div\n data-aui=\"carousel\"\n role={label ? \"region\" : \"group\"}\n aria-roledescription=\"carousel\"\n aria-label={label}\n tabIndex={0}\n >\n {cards.map((card, i) => (\n <div\n key={i}\n data-aui=\"carousel-slide\"\n role=\"group\"\n aria-roledescription=\"slide\"\n aria-label={`${i + 1} of ${n}`}\n >\n {card}\n </div>\n ))}\n </div>\n );\n },\n },\n} satisfies GenerativeUILibrary;\n"],"mappings":";;;;;AAKA,MAAM,YAAY;AAElB,MAAa,kBAAkB;CAC7B,OAAO;EACL,aACE;EACF,YAAY,EAAE,OAAO;GACnB,OAAO,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,cAAc;GACpD,aAAa,EACV,OAAO,CAAC,CACR,SAAS,CAAC,CACV,SAAS,8BAA8B;GAC1C,MAAM,EACH,KAAK,WAAW,CAAC,CACjB,SAAS,CAAC,CACV,SAAS,oCAAoC;EAClD,CAAC;EACD,SAAS,EAAE,OAAO,aAAa,MAAM,eACnC,qBAAC,OAAD;GAAK,YAAS;GAAQ,iBAAe,QAAQ;GAAQ,MAAK;aAA1D;IACG,QAAQ,oBAAC,UAAD;KAAQ,YAAS;eAAe;IAAc,CAAA,IAAI;IAC1D,cAAc,oBAAC,KAAD;KAAG,YAAS;eAAc;IAAe,CAAA,IAAI;IAC3D;GACE;;CAET;CACA,UAAU;EACR,aACE;EACF,YAAY,EAAE,OAAO,EACnB,OAAO,EACJ,OAAO,CAAC,CACR,SAAS,CAAC,CACV,SAAS,oCAAoC,EAClD,CAAC;EACD,SAAS,EAAE,OAAO,eAAe;GAC/B,MAAM,QAAQ,SAAS,QAAQ,QAAQ,CAAC,CAAC,MAAM,GAAG,SAAS;GAC3D,MAAM,IAAI,MAAM;GAChB,OACE,oBAAC,OAAD;IACE,YAAS;IACT,MAAM,QAAQ,WAAW;IACzB,wBAAqB;IACrB,cAAY;IACZ,UAAU;cAET,MAAM,KAAK,MAAM,MAChB,oBAAC,OAAD;KAEE,YAAS;KACT,MAAK;KACL,wBAAqB;KACrB,cAAY,GAAG,IAAI,EAAE,MAAM;eAE1B;IACE,GAPE,CAOF,CACN;GACE,CAAA;EAET;CACF;AACF"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { FormEvent } from "react";
|
|
2
|
+
//#region src/vocabulary/collectFormValues.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* The subset of `HTMLInputElement`/`HTMLSelectElement`/`HTMLTextAreaElement` that {@link collectFormValues} reads. A structural type rather than the DOM interfaces themselves, so a plain object can stand in for a form control in tests.
|
|
5
|
+
*/
|
|
6
|
+
type FormControlElementLike = {
|
|
7
|
+
readonly name: string;
|
|
8
|
+
readonly type: string;
|
|
9
|
+
readonly value: string;
|
|
10
|
+
readonly checked?: boolean;
|
|
11
|
+
readonly disabled: boolean;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Collects a submitted form's named control values into a plain object, keyed by `name`, in document order. Reads each control's live DOM state rather than `FormData`, so a checkbox resolves to its `checked` boolean instead of an on/off string. A radio group resolves to its checked option's `value`, or `undefined` if none is checked. Any other repeated `name` resolves to an array of its controls' values, in document order. Controls without a `name`, or that are `disabled`, are skipped entirely.
|
|
15
|
+
*/
|
|
16
|
+
declare function collectFormValues(elements: ArrayLike<FormControlElementLike>): Record<string, unknown>;
|
|
17
|
+
/**
|
|
18
|
+
* Convenience wrapper around {@link collectFormValues} for a native form submit event; the cast from `HTMLFormControlsCollection` to `ArrayLike<FormControlElementLike>` lives here once instead of at every call site.
|
|
19
|
+
*/
|
|
20
|
+
declare function collectFormValuesFromEvent(event: FormEvent<HTMLFormElement>): Record<string, unknown>;
|
|
21
|
+
//#endregion
|
|
22
|
+
export { FormControlElementLike, collectFormValues, collectFormValuesFromEvent };
|
|
23
|
+
//# sourceMappingURL=collectFormValues.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collectFormValues.d.ts","names":[],"sources":["../../src/vocabulary/collectFormValues.ts"],"mappings":";;;;;KAKY;WACD;WACA;WACA;WACA;WACA;;;;;iBAMK,kBACd,UAAU,UAAU,0BACnB;;;;iBAgCa,2BACd,OAAO,UAAU,mBAChB"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
//#region src/vocabulary/collectFormValues.ts
|
|
2
|
+
/**
|
|
3
|
+
* Collects a submitted form's named control values into a plain object, keyed by `name`, in document order. Reads each control's live DOM state rather than `FormData`, so a checkbox resolves to its `checked` boolean instead of an on/off string. A radio group resolves to its checked option's `value`, or `undefined` if none is checked. Any other repeated `name` resolves to an array of its controls' values, in document order. Controls without a `name`, or that are `disabled`, are skipped entirely.
|
|
4
|
+
*/
|
|
5
|
+
function collectFormValues(elements) {
|
|
6
|
+
const values = Object.create(null);
|
|
7
|
+
for (const element of Array.from(elements)) {
|
|
8
|
+
const { name, disabled } = element;
|
|
9
|
+
if (!name || disabled) continue;
|
|
10
|
+
if (element.type === "radio") {
|
|
11
|
+
if (element.checked) values[name] = element.value;
|
|
12
|
+
else if (!Object.hasOwn(values, name)) values[name] = void 0;
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
const value = element.type === "checkbox" ? element.checked ?? false : element.value;
|
|
16
|
+
if (Object.hasOwn(values, name)) {
|
|
17
|
+
const existing = values[name];
|
|
18
|
+
values[name] = Array.isArray(existing) ? [...existing, value] : [existing, value];
|
|
19
|
+
} else values[name] = value;
|
|
20
|
+
}
|
|
21
|
+
return values;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Convenience wrapper around {@link collectFormValues} for a native form submit event; the cast from `HTMLFormControlsCollection` to `ArrayLike<FormControlElementLike>` lives here once instead of at every call site.
|
|
25
|
+
*/
|
|
26
|
+
function collectFormValuesFromEvent(event) {
|
|
27
|
+
return collectFormValues(event.currentTarget.elements);
|
|
28
|
+
}
|
|
29
|
+
//#endregion
|
|
30
|
+
export { collectFormValues, collectFormValuesFromEvent };
|
|
31
|
+
|
|
32
|
+
//# sourceMappingURL=collectFormValues.js.map
|