chat
Version:
Unified chat abstraction for Slack, Teams, Google Chat, and Discord
1,154 lines (1,149 loc) • 31.2 kB
JavaScript
// src/markdown.ts
import { toString as mdastToString } from "mdast-util-to-string";
import remarkGfm from "remark-gfm";
import remarkParse from "remark-parse";
import remarkStringify from "remark-stringify";
import { unified } from "unified";
function isTextNode(node) {
return node.type === "text";
}
function isParagraphNode(node) {
return node.type === "paragraph";
}
function isStrongNode(node) {
return node.type === "strong";
}
function isEmphasisNode(node) {
return node.type === "emphasis";
}
function isDeleteNode(node) {
return node.type === "delete";
}
function isInlineCodeNode(node) {
return node.type === "inlineCode";
}
function isCodeNode(node) {
return node.type === "code";
}
function isLinkNode(node) {
return node.type === "link";
}
function isBlockquoteNode(node) {
return node.type === "blockquote";
}
function isListNode(node) {
return node.type === "list";
}
function isListItemNode(node) {
return node.type === "listItem";
}
function isTableNode(node) {
return node.type === "table";
}
function isTableRowNode(node) {
return node.type === "tableRow";
}
function isTableCellNode(node) {
return node.type === "tableCell";
}
function tableToAscii(node) {
const rows = [];
for (const row of node.children) {
const cells = [];
for (const cell of row.children) {
cells.push(mdastToString(cell));
}
rows.push(cells);
}
if (rows.length === 0) {
return "";
}
const headers = rows[0];
const dataRows = rows.slice(1);
return tableElementToAscii(headers, dataRows);
}
function tableElementToAscii(headers, rows) {
const allRows = [headers, ...rows];
const colCount = Math.max(...allRows.map((r) => r.length));
if (colCount === 0) {
return "";
}
const colWidths = Array.from({ length: colCount }, () => 0);
for (const row of allRows) {
for (let i = 0; i < colCount; i++) {
const cellLen = (row[i] || "").length;
if (cellLen > colWidths[i]) {
colWidths[i] = cellLen;
}
}
}
const formatRow = (cells) => Array.from(
{ length: colCount },
(_, i) => (cells[i] || "").padEnd(colWidths[i])
).join(" | ").trimEnd();
const lines = [];
lines.push(formatRow(headers));
lines.push(colWidths.map((w) => "-".repeat(w)).join("-|-"));
for (const row of rows) {
lines.push(formatRow(row));
}
return lines.join("\n");
}
function getNodeChildren(node) {
if ("children" in node && Array.isArray(node.children)) {
return node.children;
}
return [];
}
function getNodeValue(node) {
if ("value" in node && typeof node.value === "string") {
return node.value;
}
return "";
}
function parseMarkdown(markdown) {
const processor = unified().use(remarkParse).use(remarkGfm);
return processor.parse(markdown);
}
function stringifyMarkdown(ast, options) {
const processor = unified().use(remarkStringify, options).use(remarkGfm);
return processor.stringify(ast);
}
function toPlainText(ast) {
return mdastToString(ast);
}
function markdownToPlainText(markdown) {
const ast = parseMarkdown(markdown);
return mdastToString(ast);
}
function walkAst(node, visitor) {
if ("children" in node && Array.isArray(node.children)) {
node.children = node.children.map((child) => {
const result = visitor(child);
if (result === null) {
return null;
}
return walkAst(result, visitor);
}).filter((n) => n !== null);
}
return node;
}
function text(value) {
return { type: "text", value };
}
function strong(children) {
return { type: "strong", children };
}
function emphasis(children) {
return { type: "emphasis", children };
}
function strikethrough(children) {
return { type: "delete", children };
}
function inlineCode(value) {
return { type: "inlineCode", value };
}
function codeBlock(value, lang) {
return { type: "code", value, lang };
}
function link(url, children, title) {
return { type: "link", url, children, title };
}
function blockquote(children) {
return { type: "blockquote", children };
}
function paragraph(children) {
return { type: "paragraph", children };
}
function root(children) {
return { type: "root", children };
}
var BaseFormatConverter = class {
renderList(node, depth, nodeConverter, unorderedBullet = "-") {
const indent = " ".repeat(depth);
const start = node.start ?? 1;
const lines = [];
for (const [i, item] of getNodeChildren(node).entries()) {
const prefix = node.ordered ? `${start + i}.` : unorderedBullet;
let isFirstContent = true;
for (const child of getNodeChildren(item)) {
if (isListNode(child)) {
lines.push(
this.renderList(child, depth + 1, nodeConverter, unorderedBullet)
);
continue;
}
const text2 = nodeConverter(child);
if (!text2.trim()) {
continue;
}
if (isFirstContent) {
lines.push(`${indent}${prefix} ${text2}`);
isFirstContent = false;
} else {
lines.push(`${indent} ${text2}`);
}
}
}
return lines.join("\n");
}
/**
* Default fallback for converting an unknown mdast node to text.
* Recursively converts children if present, otherwise extracts the node value.
* Adapters should call this in their nodeToX() default case.
*/
defaultNodeToText(node, nodeConverter) {
const children = getNodeChildren(node);
if (children.length > 0) {
return children.map(nodeConverter).join("");
}
return getNodeValue(node);
}
/**
* Template method for implementing fromAst with a node converter.
* Iterates through AST children and converts each using the provided function.
* Joins results with double newlines (standard paragraph separation).
*
* @param ast - The AST to convert
* @param nodeConverter - Function to convert each Content node to string
* @returns Platform-formatted string
*/
fromAstWithNodeConverter(ast, nodeConverter) {
const parts = [];
for (const node of ast.children) {
parts.push(nodeConverter(node));
}
return parts.join("\n\n");
}
extractPlainText(platformText) {
return toPlainText(this.toAst(platformText));
}
// Convenience methods for markdown string I/O
fromMarkdown(markdown) {
return this.fromAst(parseMarkdown(markdown));
}
toMarkdown(platformText) {
return stringifyMarkdown(this.toAst(platformText));
}
/** @deprecated Use extractPlainText instead */
toPlainText(platformText) {
return this.extractPlainText(platformText);
}
/**
* Convert a PostableMessage to platform format (text only).
* - string: passed through as raw text (no conversion)
* - { raw: string }: passed through as raw text (no conversion)
* - { markdown: string }: converted from markdown to platform format
* - { ast: Root }: converted from AST to platform format
* - { card: CardElement }: returns fallback text (cards should be handled by adapter)
* - CardElement: returns fallback text (cards should be handled by adapter)
*
* Note: For cards, adapters should check for card content first and render
* them using platform-specific card APIs, using this method only for fallback.
*/
renderPostable(message) {
if (typeof message === "string") {
return message;
}
if ("raw" in message) {
return message.raw;
}
if ("markdown" in message) {
return this.fromMarkdown(message.markdown);
}
if ("ast" in message) {
return this.fromAst(message.ast);
}
if ("card" in message) {
return message.fallbackText || this.cardToFallbackText(message.card);
}
if ("type" in message && message.type === "card") {
return this.cardToFallbackText(message);
}
throw new Error("Invalid PostableMessage format");
}
/**
* Generate fallback text from a card element.
* Override in subclasses for platform-specific formatting.
*/
cardToFallbackText(card) {
const parts = [];
if (card.title) {
parts.push(`**${card.title}**`);
}
if (card.subtitle) {
parts.push(card.subtitle);
}
for (const child of card.children) {
const text2 = this.cardChildToFallbackText(child);
if (text2) {
parts.push(text2);
}
}
return parts.join("\n");
}
/**
* Convert card child element to fallback text.
*/
cardChildToFallbackText(child) {
switch (child.type) {
case "text":
return child.content;
case "fields":
return child.children.map((f) => `**${f.label}**: ${f.value}`).join("\n");
case "actions":
return null;
case "table":
return tableElementToAscii(child.headers, child.rows);
case "section":
return child.children.map((c) => this.cardChildToFallbackText(c)).filter(Boolean).join("\n");
default:
return null;
}
}
};
// src/cards.ts
function isCardElement(value) {
return typeof value === "object" && value !== null && "type" in value && value.type === "card";
}
function Card(options = {}) {
return {
type: "card",
title: options.title,
subtitle: options.subtitle,
imageUrl: options.imageUrl,
children: options.children ?? []
};
}
function Text(content, options = {}) {
return {
type: "text",
content,
style: options.style
};
}
var CardText = Text;
function Image(options) {
return {
type: "image",
url: options.url,
alt: options.alt
};
}
function Divider() {
return { type: "divider" };
}
function Section(children) {
return {
type: "section",
children
};
}
function Actions(children) {
return {
type: "actions",
children
};
}
function Button(options) {
return {
type: "button",
id: options.id,
label: options.label,
style: options.style,
value: options.value,
disabled: options.disabled,
actionType: options.actionType,
callbackUrl: options.callbackUrl
};
}
function LinkButton(options) {
return {
type: "link-button",
url: options.url,
label: options.label,
style: options.style
};
}
function Field(options) {
return {
type: "field",
label: options.label,
value: options.value
};
}
function Fields(children) {
return {
type: "fields",
children
};
}
function Table(options) {
return {
type: "table",
headers: options.headers,
rows: options.rows,
align: options.align
};
}
function CardLink(options) {
return {
type: "link",
url: options.url,
label: options.label
};
}
function isReactElement(value) {
if (typeof value !== "object" || value === null) {
return false;
}
const maybeElement = value;
if (typeof maybeElement.$$typeof !== "symbol") {
return false;
}
const symbolStr = maybeElement.$$typeof.toString();
return symbolStr.includes("react.element") || symbolStr.includes("react.transitional.element");
}
var componentMap = /* @__PURE__ */ new Map([
[Card, "Card"],
[Text, "Text"],
[Image, "Image"],
[Divider, "Divider"],
[Section, "Section"],
[Actions, "Actions"],
[Button, "Button"],
[LinkButton, "LinkButton"],
[CardLink, "CardLink"],
[Field, "Field"],
[Fields, "Fields"],
[Table, "Table"]
]);
function fromReactElement(element) {
if (!isReactElement(element)) {
if (isCardElement(element)) {
return element;
}
if (typeof element === "object" && element !== null && "type" in element) {
return element;
}
return null;
}
const { type, props } = element;
const componentName = componentMap.get(type);
if (!componentName) {
if (typeof type === "string") {
throw new Error(
`HTML element <${type}> is not supported in card elements. Use Card, Text, Section, Actions, Button, Fields, Field, Image, or Divider components instead.`
);
}
if (props.children) {
return convertChildren(props.children)[0] ?? null;
}
return null;
}
const convertedChildren = props.children ? convertChildren(props.children) : [];
const isCardChild = (el) => el.type !== "card" && el.type !== "button" && el.type !== "link-button" && el.type !== "field" && el.type !== "select" && el.type !== "radio_select";
switch (componentName) {
case "Card":
return Card({
title: props.title,
subtitle: props.subtitle,
imageUrl: props.imageUrl,
children: convertedChildren.filter(isCardChild)
});
case "Text": {
const content = extractTextContent(props.children);
return Text(content, { style: props.style });
}
case "Image":
return Image({
url: props.url,
alt: props.alt
});
case "Divider":
return Divider();
case "Section":
return Section(convertedChildren.filter(isCardChild));
case "Actions":
return Actions(
convertedChildren.filter(
(c) => c.type === "button" || c.type === "link-button" || c.type === "select" || c.type === "radio_select"
)
);
case "Button": {
const label = extractTextContent(props.children);
return Button({
id: props.id,
label: props.label ?? label,
style: props.style,
value: props.value,
actionType: props.actionType,
disabled: props.disabled
});
}
case "LinkButton": {
const label = extractTextContent(props.children);
return LinkButton({
url: props.url,
label: props.label ?? label,
style: props.style
});
}
case "CardLink": {
const label = extractTextContent(props.children);
return CardLink({
url: props.url,
label: props.label ?? label
});
}
case "Field":
return Field({
label: props.label,
value: props.value
});
case "Fields":
return Fields(
convertedChildren.filter((c) => c.type === "field")
);
case "Table":
return Table({
headers: props.headers,
rows: props.rows,
align: props.align
});
default:
return null;
}
}
function convertChildren(children) {
if (children == null) {
return [];
}
if (Array.isArray(children)) {
return children.flatMap(convertChildren);
}
const converted = fromReactElement(children);
if (converted && typeof converted === "object" && "type" in converted) {
if (converted.type === "card") {
return converted.children;
}
return [converted];
}
return [];
}
function extractTextContent(children) {
if (typeof children === "string") {
return children;
}
if (typeof children === "number") {
return String(children);
}
if (Array.isArray(children)) {
return children.map(extractTextContent).join("");
}
return "";
}
function cardToFallbackText(card) {
const parts = [];
if (card.title) {
parts.push(`**${card.title}**`);
}
if (card.subtitle) {
parts.push(card.subtitle);
}
for (const child of card.children) {
const text2 = cardChildToFallbackText(child);
if (text2) {
parts.push(text2);
}
}
return parts.join("\n");
}
function cardChildToFallbackText(child) {
switch (child.type) {
case "text":
return child.content;
case "link":
return `${child.label} (${child.url})`;
case "fields":
return child.children.map((f) => `${f.label}: ${f.value}`).join("\n");
case "actions":
return null;
case "table":
return tableElementToAscii(child.headers, child.rows);
case "section":
return child.children.map((c) => cardChildToFallbackText(c)).filter(Boolean).join("\n");
default:
return null;
}
}
// src/modals.ts
var VALID_MODAL_CHILD_TYPES = [
"text_input",
"select",
"external_select",
"radio_select",
"text",
"fields"
];
function isModalElement(value) {
return typeof value === "object" && value !== null && "type" in value && value.type === "modal";
}
function filterModalChildren(children) {
const validChildren = children.filter(
(c) => typeof c === "object" && c !== null && "type" in c && VALID_MODAL_CHILD_TYPES.includes(
c.type
)
);
if (validChildren.length < children.length) {
console.warn(
"[chat] Modal contains unsupported child elements that were ignored"
);
}
return validChildren;
}
function Modal(options) {
return {
type: "modal",
callbackId: options.callbackId,
callbackUrl: options.callbackUrl,
title: options.title,
submitLabel: options.submitLabel,
closeLabel: options.closeLabel,
notifyOnClose: options.notifyOnClose,
privateMetadata: options.privateMetadata,
children: options.children ?? []
};
}
function TextInput(options) {
return {
type: "text_input",
id: options.id,
label: options.label,
placeholder: options.placeholder,
initialValue: options.initialValue,
multiline: options.multiline,
optional: options.optional,
maxLength: options.maxLength
};
}
function Select(options) {
if (!options.options || options.options.length === 0) {
throw new Error("Select requires at least one option");
}
return {
type: "select",
id: options.id,
label: options.label,
placeholder: options.placeholder,
options: options.options,
initialOption: options.initialOption,
optional: options.optional
};
}
function ExternalSelect(options) {
return {
type: "external_select",
id: options.id,
initialOption: options.initialOption,
label: options.label,
placeholder: options.placeholder,
minQueryLength: options.minQueryLength,
optional: options.optional
};
}
function SelectOption(options) {
return {
label: options.label,
value: options.value,
description: options.description
};
}
function RadioSelect(options) {
if (!options.options || options.options.length === 0) {
throw new Error("RadioSelect requires at least one option");
}
return {
type: "radio_select",
id: options.id,
label: options.label,
options: options.options,
initialOption: options.initialOption,
optional: options.optional
};
}
function isReactElement2(value) {
if (typeof value !== "object" || value === null) {
return false;
}
const maybeElement = value;
if (typeof maybeElement.$$typeof !== "symbol") {
return false;
}
const symbolStr = maybeElement.$$typeof.toString();
return symbolStr.includes("react.element") || symbolStr.includes("react.transitional.element");
}
var modalComponentMap = /* @__PURE__ */ new Map([
[Modal, "Modal"],
[TextInput, "TextInput"],
[Select, "Select"],
[ExternalSelect, "ExternalSelect"],
[RadioSelect, "RadioSelect"],
[SelectOption, "SelectOption"]
]);
function fromReactModalElement(element) {
if (!isReactElement2(element)) {
if (isModalElement(element)) {
return element;
}
if (typeof element === "object" && element !== null && "type" in element) {
return element;
}
return null;
}
const { type, props } = element;
const componentName = modalComponentMap.get(type);
if (!componentName) {
if (props.children) {
return convertModalChildren(props.children)[0] ?? null;
}
return null;
}
const convertedChildren = props.children ? convertModalChildren(props.children) : [];
switch (componentName) {
case "Modal":
return Modal({
callbackId: props.callbackId,
title: props.title,
submitLabel: props.submitLabel,
closeLabel: props.closeLabel,
notifyOnClose: props.notifyOnClose,
privateMetadata: props.privateMetadata,
children: filterModalChildren(convertedChildren)
});
case "TextInput":
return TextInput({
id: props.id,
label: props.label,
placeholder: props.placeholder,
initialValue: props.initialValue,
multiline: props.multiline,
optional: props.optional,
maxLength: props.maxLength
});
case "Select":
return Select({
id: props.id,
label: props.label,
placeholder: props.placeholder,
options: convertedChildren.filter(
(c) => c !== null && "label" in c && "value" in c && !("type" in c)
),
initialOption: props.initialOption,
optional: props.optional
});
case "ExternalSelect":
return ExternalSelect({
id: props.id,
initialOption: props.initialOption,
label: props.label,
placeholder: props.placeholder,
minQueryLength: props.minQueryLength,
optional: props.optional
});
case "RadioSelect":
return RadioSelect({
id: props.id,
label: props.label,
options: convertedChildren.filter(
(c) => c !== null && "label" in c && "value" in c && !("type" in c)
),
initialOption: props.initialOption,
optional: props.optional
});
case "SelectOption":
return SelectOption({
label: props.label,
value: props.value,
description: props.description
});
default:
return null;
}
}
function convertModalChildren(children) {
if (children == null) {
return [];
}
if (Array.isArray(children)) {
return children.flatMap(convertModalChildren);
}
const converted = fromReactModalElement(children);
if (converted) {
if (isModalElement(converted)) {
return converted.children;
}
return [converted];
}
return [];
}
// src/jsx-runtime.ts
var JSX_ELEMENT = /* @__PURE__ */ Symbol.for("chat.jsx.element");
function isJSXElement(value) {
return typeof value === "object" && value !== null && value.$$typeof === JSX_ELEMENT;
}
function processChildren(children) {
if (children == null) {
return [];
}
if (Array.isArray(children)) {
return children.flatMap(processChildren);
}
if (isJSXElement(children)) {
const resolved = resolveJSXElement(children);
if (resolved) {
return [resolved];
}
return [];
}
if (typeof children === "object" && "type" in children) {
return [children];
}
if (typeof children === "string" || typeof children === "number") {
return [String(children)];
}
return [];
}
function isTextProps(props) {
return !("id" in props || "url" in props || "label" in props);
}
function isButtonProps(props) {
return "id" in props && typeof props.id === "string" && !("url" in props);
}
function isLinkButtonProps(props) {
return "url" in props && typeof props.url === "string" && !("id" in props);
}
function isCardLinkProps(props) {
return "url" in props && typeof props.url === "string" && !("id" in props) && !("alt" in props) && !("style" in props);
}
function isImageProps(props) {
return "url" in props && typeof props.url === "string";
}
function isFieldProps(props) {
return "label" in props && "value" in props && typeof props.label === "string" && typeof props.value === "string";
}
function isCardProps(props) {
return !("id" in props || "url" in props || "callbackId" in props) && ("title" in props || "subtitle" in props || "imageUrl" in props);
}
function isModalProps(props) {
return "callbackId" in props && "title" in props;
}
function isTextInputProps(props) {
return "id" in props && "label" in props && !("options" in props) && !("value" in props);
}
function isSelectProps(props) {
return "id" in props && "label" in props && !("value" in props);
}
function isExternalSelectProps(props) {
return "id" in props && "label" in props && !("value" in props) && !("children" in props);
}
function isSelectOptionProps(props) {
return "label" in props && "value" in props && !("id" in props);
}
function resolveJSXElement(element) {
const { type, props, children } = element;
const processedChildren = processChildren(children);
if (type === Text) {
const textProps = isTextProps(props) ? props : { style: void 0 };
const content = processedChildren.length > 0 ? processedChildren.map(String).join("") : String(textProps.children ?? "");
return Text(content, { style: textProps.style });
}
if (type === Section) {
return Section(processedChildren);
}
if (type === Actions) {
return Actions(
processedChildren
);
}
if (type === Fields) {
return Fields(processedChildren);
}
if (type === Button) {
if (!isButtonProps(props)) {
throw new Error("Button requires an 'id' prop");
}
const label = processedChildren.length > 0 ? processedChildren.map(String).join("") : props.label ?? "";
return Button({
id: props.id,
label,
style: props.style,
value: props.value,
actionType: props.actionType,
callbackUrl: props.callbackUrl,
disabled: props.disabled
});
}
if (type === LinkButton) {
if (!isLinkButtonProps(props)) {
throw new Error("LinkButton requires a 'url' prop");
}
const label = processedChildren.length > 0 ? processedChildren.map(String).join("") : props.label ?? "";
return LinkButton({
url: props.url,
label,
style: props.style
});
}
if (type === CardLink) {
if (!isCardLinkProps(props)) {
throw new Error("CardLink requires a 'url' prop");
}
const label = processedChildren.length > 0 ? processedChildren.map(String).join("") : props.label ?? "";
return CardLink({
url: props.url,
label
});
}
if (type === Image) {
if (!isImageProps(props)) {
throw new Error("Image requires a 'url' prop");
}
return Image({ url: props.url, alt: props.alt });
}
if (type === Field) {
if (!isFieldProps(props)) {
throw new Error("Field requires 'label' and 'value' props");
}
return Field({
label: props.label,
value: props.value
});
}
if (type === Divider) {
return Divider();
}
if (type === Modal) {
if (!isModalProps(props)) {
throw new Error("Modal requires 'callbackId' and 'title' props");
}
return Modal({
callbackId: props.callbackId,
callbackUrl: props.callbackUrl,
title: props.title,
submitLabel: props.submitLabel,
closeLabel: props.closeLabel,
notifyOnClose: props.notifyOnClose,
privateMetadata: props.privateMetadata,
children: filterModalChildren(processedChildren)
});
}
if (type === TextInput) {
if (!isTextInputProps(props)) {
throw new Error("TextInput requires 'id' and 'label' props");
}
return TextInput({
id: props.id,
label: props.label,
placeholder: props.placeholder,
initialValue: props.initialValue,
multiline: props.multiline,
optional: props.optional,
maxLength: props.maxLength
});
}
if (type === Select) {
if (!isSelectProps(props)) {
throw new Error("Select requires 'id' and 'label' props");
}
return Select({
id: props.id,
label: props.label,
placeholder: props.placeholder,
initialOption: props.initialOption,
optional: props.optional,
options: processedChildren
});
}
if (type === ExternalSelect) {
if (!isExternalSelectProps(props)) {
throw new Error("ExternalSelect requires 'id' and 'label' props");
}
return ExternalSelect({
id: props.id,
initialOption: props.initialOption,
label: props.label,
placeholder: props.placeholder,
minQueryLength: props.minQueryLength,
optional: props.optional
});
}
if (type === RadioSelect) {
if (!isSelectProps(props)) {
throw new Error("RadioSelect requires 'id' and 'label' props");
}
return RadioSelect({
id: props.id,
label: props.label,
initialOption: props.initialOption,
optional: props.optional,
options: processedChildren
});
}
if (type === SelectOption) {
if (!isSelectOptionProps(props)) {
throw new Error("SelectOption requires 'label' and 'value' props");
}
return SelectOption({
label: props.label,
value: props.value,
description: props.description
});
}
if (type === Table) {
const tableProps = props;
return Table({
headers: tableProps.headers,
rows: tableProps.rows
});
}
const cardProps = isCardProps(props) ? props : {};
return Card({
title: cardProps.title,
subtitle: cardProps.subtitle,
imageUrl: cardProps.imageUrl,
children: processedChildren
});
}
function jsx(type, props, _key) {
const { children, ...restProps } = props;
return {
$$typeof: JSX_ELEMENT,
type,
props: restProps,
children: children != null ? [children] : []
};
}
function jsxs(type, props, _key) {
const { children, ...restProps } = props;
let resolvedChildren;
if (Array.isArray(children)) {
resolvedChildren = children;
} else if (children != null) {
resolvedChildren = [children];
} else {
resolvedChildren = [];
}
return {
$$typeof: JSX_ELEMENT,
type,
props: restProps,
children: resolvedChildren
};
}
var jsxDEV = jsx;
function Fragment(props) {
return processChildren(props.children);
}
function toCardElement(jsxElement) {
if (isJSXElement(jsxElement)) {
const resolved = resolveJSXElement(jsxElement);
if (resolved && typeof resolved === "object" && "type" in resolved && resolved.type === "card") {
return resolved;
}
}
if (typeof jsxElement === "object" && jsxElement !== null && "type" in jsxElement && jsxElement.type === "card") {
return jsxElement;
}
return null;
}
function toModalElement(jsxElement) {
if (isJSXElement(jsxElement)) {
const resolved = resolveJSXElement(jsxElement);
if (resolved && typeof resolved === "object" && "type" in resolved && resolved.type === "modal") {
return resolved;
}
}
if (isModalElement(jsxElement)) {
return jsxElement;
}
return null;
}
function isJSX(value) {
if (isJSXElement(value)) {
return true;
}
if (typeof value === "object" && value !== null && "$$typeof" in value && typeof value.$$typeof === "symbol") {
const symbolStr = value.$$typeof.toString();
return symbolStr.includes("react.element") || symbolStr.includes("react.transitional.element");
}
return false;
}
export {
isTextNode,
isParagraphNode,
isStrongNode,
isEmphasisNode,
isDeleteNode,
isInlineCodeNode,
isCodeNode,
isLinkNode,
isBlockquoteNode,
isListNode,
isListItemNode,
isTableNode,
isTableRowNode,
isTableCellNode,
tableToAscii,
tableElementToAscii,
getNodeChildren,
getNodeValue,
parseMarkdown,
stringifyMarkdown,
toPlainText,
markdownToPlainText,
walkAst,
text,
strong,
emphasis,
strikethrough,
inlineCode,
codeBlock,
link,
blockquote,
paragraph,
root,
BaseFormatConverter,
isCardElement,
Card,
CardText,
Image,
Divider,
Section,
Actions,
Button,
LinkButton,
Field,
Fields,
Table,
CardLink,
fromReactElement,
cardToFallbackText,
cardChildToFallbackText,
isModalElement,
Modal,
TextInput,
Select,
ExternalSelect,
SelectOption,
RadioSelect,
fromReactModalElement,
isCardLinkProps,
jsx,
jsxs,
jsxDEV,
Fragment,
toCardElement,
toModalElement,
isJSX
};