onScrollToBottom: () => void
onSubmit: (input: string) => void
@@ -56,6 +57,7 @@ export const ChatChunk = (props: Props) => {
>
{
)
for (const action of actionsBeforeFirstBubble) {
const response = await executeClientSideAction(action)
- if (response) setBlockedPopupUrl(response.blockedPopupUrl)
+ if (response && 'replyToSend' in response) {
+ sendMessage(response.replyToSend)
+ return
+ }
+ if (response && 'blockedPopupUrl' in response)
+ setBlockedPopupUrl(response.blockedPopupUrl)
}
}
})()
@@ -122,7 +127,12 @@ export const ConversationContainer = (props: Props) => {
)
for (const action of actionsBeforeFirstBubble) {
const response = await executeClientSideAction(action)
- if (response) setBlockedPopupUrl(response.blockedPopupUrl)
+ if (response && 'replyToSend' in response) {
+ sendMessage(response.replyToSend)
+ return
+ }
+ if (response && 'blockedPopupUrl' in response)
+ setBlockedPopupUrl(response.blockedPopupUrl)
}
}
setChatChunks((displayedChunks) => [
@@ -159,7 +169,12 @@ export const ConversationContainer = (props: Props) => {
)
for (const action of actionsToExecute) {
const response = await executeClientSideAction(action)
- if (response) setBlockedPopupUrl(response.blockedPopupUrl)
+ if (response && 'replyToSend' in response) {
+ sendMessage(response.replyToSend)
+ return
+ }
+ if (response && 'blockedPopupUrl' in response)
+ setBlockedPopupUrl(response.blockedPopupUrl)
}
}
}
@@ -187,6 +202,7 @@ export const ConversationContainer = (props: Props) => {
onSkip={handleSkip}
context={props.context}
hasError={hasError() && index() === chatChunks().length - 1}
+ hideAvatar={!chatChunk.input && index() < chatChunks().length - 1}
/>
)}
diff --git a/packages/embeds/js/src/features/blocks/logic/setVariable/executeSetVariable.ts b/packages/embeds/js/src/features/blocks/logic/setVariable/executeSetVariable.ts
new file mode 100644
index 00000000000..151e5abe6e1
--- /dev/null
+++ b/packages/embeds/js/src/features/blocks/logic/setVariable/executeSetVariable.ts
@@ -0,0 +1,33 @@
+import { isNotDefined } from '@typebot.io/lib'
+import type { ScriptToExecute } from '@typebot.io/schemas'
+
+export const executeSetVariable = async ({
+ content,
+ args,
+}: ScriptToExecute): Promise<{ replyToSend: string | undefined }> => {
+ try {
+ const func = Function(
+ ...args.map((arg) => arg.id),
+ content.includes('return ') ? content : `return ${content}`
+ )
+ const replyToSend = await func(...args.map((arg) => arg.value))
+ return {
+ replyToSend: safeStringify(replyToSend),
+ }
+ } catch (err) {
+ return {
+ replyToSend: safeStringify(content),
+ }
+ }
+}
+
+export const safeStringify = (val: unknown): string | undefined => {
+ if (isNotDefined(val)) return
+ if (typeof val === 'string') return val
+ try {
+ return JSON.stringify(val)
+ } catch {
+ console.warn('Failed to safely stringify variable value', val)
+ return
+ }
+}
diff --git a/packages/embeds/js/src/utils/executeClientSideActions.ts b/packages/embeds/js/src/utils/executeClientSideActions.ts
index aa8d8034235..a6cc45df35b 100644
--- a/packages/embeds/js/src/utils/executeClientSideActions.ts
+++ b/packages/embeds/js/src/utils/executeClientSideActions.ts
@@ -2,12 +2,15 @@ import { executeChatwoot } from '@/features/blocks/integrations/chatwoot'
import { executeGoogleAnalyticsBlock } from '@/features/blocks/integrations/googleAnalytics/utils/executeGoogleAnalytics'
import { executeRedirect } from '@/features/blocks/logic/redirect'
import { executeScript } from '@/features/blocks/logic/script/executeScript'
+import { executeSetVariable } from '@/features/blocks/logic/setVariable/executeSetVariable'
import { executeWait } from '@/features/blocks/logic/wait/utils/executeWait'
import type { ChatReply } from '@typebot.io/schemas'
export const executeClientSideAction = async (
clientSideAction: NonNullable[0]
-): Promise<{ blockedPopupUrl: string } | void> => {
+): Promise<
+ { blockedPopupUrl: string } | { replyToSend: string | undefined } | void
+> => {
if ('chatwoot' in clientSideAction) {
return executeChatwoot(clientSideAction.chatwoot)
}
@@ -23,4 +26,7 @@ export const executeClientSideAction = async (
if ('wait' in clientSideAction) {
return executeWait(clientSideAction.wait)
}
+ if ('setVariable' in clientSideAction) {
+ return executeSetVariable(clientSideAction.setVariable.scriptToExecute)
+ }
}
diff --git a/packages/embeds/react/package.json b/packages/embeds/react/package.json
index 29a3c3c2c29..6cc1fa06da6 100644
--- a/packages/embeds/react/package.json
+++ b/packages/embeds/react/package.json
@@ -1,6 +1,6 @@
{
"name": "@typebot.io/react",
- "version": "0.0.35",
+ "version": "0.0.36",
"description": "React library to display typebots on your website",
"main": "dist/index.js",
"types": "dist/index.d.ts",
diff --git a/packages/schemas/features/blocks/logic/setVariable.ts b/packages/schemas/features/blocks/logic/setVariable.ts
index 8468ea57ad8..f7e635c82ec 100644
--- a/packages/schemas/features/blocks/logic/setVariable.ts
+++ b/packages/schemas/features/blocks/logic/setVariable.ts
@@ -6,6 +6,7 @@ export const setVariableOptionsSchema = z.object({
variableId: z.string().optional(),
expressionToEvaluate: z.string().optional(),
isCode: z.boolean().optional(),
+ isExecutedOnClient: z.boolean().optional(),
})
export const setVariableBlockSchema = blockBaseSchema.merge(
diff --git a/packages/schemas/features/chat.ts b/packages/schemas/features/chat.ts
index c433edc6cf7..512dd2c8002 100644
--- a/packages/schemas/features/chat.ts
+++ b/packages/schemas/features/chat.ts
@@ -220,6 +220,11 @@ const clientSideActionSchema = z
}),
})
)
+ .or(
+ z.object({
+ setVariable: z.object({ scriptToExecute: scriptToExecuteSchema }),
+ })
+ )
)
export const chatReplySchema = z.object({