200 lines
5.5 KiB
TypeScript
200 lines
5.5 KiB
TypeScript
import { parseVideoUrl } from '@typebot.io/lib/parseVideoUrl'
|
|
import {
|
|
BubbleBlock,
|
|
Variable,
|
|
ChatReply,
|
|
BubbleBlockType,
|
|
} from '@typebot.io/schemas'
|
|
import { deepParseVariables } from './variables/deepParseVariables'
|
|
import { isEmpty, isNotEmpty } from '@typebot.io/lib/utils'
|
|
import {
|
|
getVariablesToParseInfoInText,
|
|
parseVariables,
|
|
} from './variables/parseVariables'
|
|
import { TDescendant, createPlateEditor } from '@udecode/plate-common'
|
|
import {
|
|
createDeserializeMdPlugin,
|
|
deserializeMd,
|
|
} from '@udecode/plate-serializer-md'
|
|
|
|
type Params = {
|
|
version: 1 | 2
|
|
variables: Variable[]
|
|
}
|
|
|
|
export const parseBubbleBlock = (
|
|
block: BubbleBlock,
|
|
{ version, variables }: Params
|
|
): ChatReply['messages'][0] => {
|
|
switch (block.type) {
|
|
case BubbleBlockType.TEXT: {
|
|
if (version === 1)
|
|
return deepParseVariables(
|
|
variables,
|
|
{},
|
|
{ takeLatestIfList: true }
|
|
)(block)
|
|
return {
|
|
...block,
|
|
content: {
|
|
...block.content,
|
|
richText: parseVariablesInRichText(block.content.richText, {
|
|
variables,
|
|
takeLatestIfList: true,
|
|
}),
|
|
},
|
|
}
|
|
}
|
|
|
|
case BubbleBlockType.EMBED: {
|
|
const message = deepParseVariables(variables)(block)
|
|
return {
|
|
...message,
|
|
content: {
|
|
...message.content,
|
|
height:
|
|
typeof message.content.height === 'string'
|
|
? parseFloat(message.content.height)
|
|
: message.content.height,
|
|
},
|
|
}
|
|
}
|
|
case BubbleBlockType.VIDEO: {
|
|
const parsedContent = deepParseVariables(variables)(block.content)
|
|
return {
|
|
...block,
|
|
content: {
|
|
...(parsedContent.url ? parseVideoUrl(parsedContent.url) : {}),
|
|
height:
|
|
typeof parsedContent.height === 'string'
|
|
? parseFloat(parsedContent.height)
|
|
: parsedContent.height,
|
|
},
|
|
}
|
|
}
|
|
default:
|
|
return deepParseVariables(variables)(block)
|
|
}
|
|
}
|
|
|
|
const parseVariablesInRichText = (
|
|
elements: TDescendant[],
|
|
{
|
|
variables,
|
|
takeLatestIfList,
|
|
}: { variables: Variable[]; takeLatestIfList?: boolean }
|
|
): TDescendant[] => {
|
|
const parsedElements: TDescendant[] = []
|
|
for (const element of elements) {
|
|
if ('text' in element) {
|
|
const text = element.text as string
|
|
if (isEmpty(text)) {
|
|
parsedElements.push(element)
|
|
continue
|
|
}
|
|
const variablesInText = getVariablesToParseInfoInText(text, {
|
|
variables,
|
|
takeLatestIfList,
|
|
})
|
|
if (variablesInText.length === 0) {
|
|
parsedElements.push(element)
|
|
continue
|
|
}
|
|
let lastTextEndIndex = 0
|
|
let index = -1
|
|
for (const variableInText of variablesInText) {
|
|
index += 1
|
|
const textBeforeVariable = text.substring(
|
|
lastTextEndIndex,
|
|
variableInText.startIndex
|
|
)
|
|
const textAfterVariable =
|
|
index === variablesInText.length - 1
|
|
? text.substring(variableInText.endIndex)
|
|
: undefined
|
|
lastTextEndIndex = variableInText.endIndex
|
|
const isStandaloneElement =
|
|
isEmpty(textBeforeVariable) && isEmpty(textAfterVariable)
|
|
const variableElements = convertMarkdownToRichText(
|
|
isStandaloneElement
|
|
? variableInText.value
|
|
: variableInText.value.replace(/[\n]+/g, ' ')
|
|
)
|
|
const variableElementsWithStyling = applyElementStyleToDescendants(
|
|
variableElements,
|
|
{
|
|
bold: element.bold,
|
|
italic: element.italic,
|
|
underline: element.underline,
|
|
}
|
|
)
|
|
|
|
if (isStandaloneElement) {
|
|
parsedElements.push(...variableElementsWithStyling)
|
|
continue
|
|
}
|
|
const children: TDescendant[] = []
|
|
if (isNotEmpty(textBeforeVariable))
|
|
children.push({
|
|
...element,
|
|
text: textBeforeVariable,
|
|
})
|
|
children.push({
|
|
type: 'inline-variable',
|
|
children: variableElementsWithStyling,
|
|
})
|
|
if (isNotEmpty(textAfterVariable))
|
|
children.push({
|
|
...element,
|
|
text: textAfterVariable,
|
|
})
|
|
parsedElements.push(...children)
|
|
}
|
|
|
|
continue
|
|
}
|
|
|
|
const type =
|
|
element.children.length === 1 &&
|
|
'text' in element.children[0] &&
|
|
(element.children[0].text as string).startsWith('{{') &&
|
|
(element.children[0].text as string).endsWith('}}') &&
|
|
element.type !== 'a'
|
|
? 'variable'
|
|
: element.type
|
|
|
|
parsedElements.push({
|
|
...element,
|
|
url: element.url
|
|
? parseVariables(variables)(element.url as string)
|
|
: undefined,
|
|
type,
|
|
children: parseVariablesInRichText(element.children as TDescendant[], {
|
|
variables,
|
|
takeLatestIfList,
|
|
}),
|
|
})
|
|
}
|
|
return parsedElements
|
|
}
|
|
|
|
const applyElementStyleToDescendants = (
|
|
variableElements: TDescendant[],
|
|
styles: { bold: unknown; italic: unknown; underline: unknown }
|
|
): TDescendant[] =>
|
|
variableElements.map((variableElement) => {
|
|
if ('text' in variableElement) return { ...styles, ...variableElement }
|
|
return {
|
|
...variableElement,
|
|
children: applyElementStyleToDescendants(
|
|
variableElement.children,
|
|
styles
|
|
),
|
|
}
|
|
})
|
|
|
|
const convertMarkdownToRichText = (text: string): TDescendant[] => {
|
|
const plugins = [createDeserializeMdPlugin()]
|
|
return deserializeMd(createPlateEditor({ plugins }) as unknown as any, text)
|
|
}
|