diff --git a/apps/builder/src/features/publish/components/embeds/EmbedButton.tsx b/apps/builder/src/features/publish/components/embeds/EmbedButton.tsx index 84242d0c8..c8aeb73f8 100644 --- a/apps/builder/src/features/publish/components/embeds/EmbedButton.tsx +++ b/apps/builder/src/features/publish/components/embeds/EmbedButton.tsx @@ -24,6 +24,8 @@ import { WixModal, } from './modals' import { OtherModal } from './modals/OtherModal' +import { ScriptIcon } from '@/features/blocks/logic/script' +import { ScriptModal } from './modals/Script/ScriptModal' export type ModalProps = { publicId: string @@ -112,6 +114,14 @@ export const integrationsList = [ {...props} /> ), + (props: Pick) => ( + } + label="Script" + Modal={ScriptModal} + {...props} + /> + ), (props: Pick) => ( } diff --git a/apps/builder/src/features/publish/components/embeds/modals/Script/ScriptModal.tsx b/apps/builder/src/features/publish/components/embeds/modals/Script/ScriptModal.tsx new file mode 100644 index 000000000..16a405a08 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/Script/ScriptModal.tsx @@ -0,0 +1,25 @@ +import React, { useState } from 'react' +import { ModalProps } from '../../EmbedButton' +import { EmbedModal } from '../../EmbedModal' +import { isDefined } from '@udecode/plate-common' +import { ScriptInstructions } from './instructions/ScriptInstructions' + +export const ScriptModal = ({ isOpen, onClose, isPublished }: ModalProps) => { + const [selectedEmbedType, setSelectedEmbedType] = useState< + 'standard' | 'popup' | 'bubble' | undefined + >() + return ( + + {isDefined(selectedEmbedType) && ( + + )} + + ) +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/Script/instructions/ScriptBubbleInstructions.tsx b/apps/builder/src/features/publish/components/embeds/modals/Script/instructions/ScriptBubbleInstructions.tsx new file mode 100644 index 000000000..aa680f0c9 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/Script/instructions/ScriptBubbleInstructions.tsx @@ -0,0 +1,55 @@ +import { CodeEditor } from '@/components/CodeEditor' +import { useTypebot } from '@/features/editor' +import { isCloudProdInstance } from '@/utils/helpers' +import { Stack, Text } from '@chakra-ui/react' +import { BubbleProps } from '@typebot.io/js' +import { Typebot } from 'models' +import { useState } from 'react' +import { env, getViewerUrl } from 'utils' +import { BubbleSettings } from '../../../settings/BubbleSettings/BubbleSettings' +import { parseInlineScript, parseInitBubbleCode } from '../../../snippetParsers' + +export const parseDefaultBubbleTheme = (typebot?: Typebot) => ({ + button: { + backgroundColor: typebot?.theme.chat.buttons.backgroundColor, + iconColor: typebot?.theme.chat.buttons.color, + }, + previewMessage: { + backgroundColor: typebot?.theme.general.background.content ?? 'white', + textColor: 'black', + }, +}) + +export const ScriptBubbleInstructions = () => { + const { typebot } = useTypebot() + const [theme, setTheme] = useState( + parseDefaultBubbleTheme(typebot) + ) + const [previewMessage, setPreviewMessage] = + useState() + + const scriptSnippet = parseInlineScript( + parseInitBubbleCode({ + typebot: typebot?.publicId ?? '', + apiHost: isCloudProdInstance + ? undefined + : env('VIEWER_INTERNAL_URL') ?? getViewerUrl(), + theme, + previewMessage, + }) + ) + + return ( + + + Run this script to initialize the typebot: + + + ) +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/Script/instructions/ScriptInstructions.tsx b/apps/builder/src/features/publish/components/embeds/modals/Script/instructions/ScriptInstructions.tsx new file mode 100644 index 000000000..11768fcfc --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/Script/instructions/ScriptInstructions.tsx @@ -0,0 +1,21 @@ +import { ScriptBubbleInstructions } from './ScriptBubbleInstructions' +import { ScriptPopupInstructions } from './ScriptPopupInstructions' +import { ScriptStandardInstructions } from './ScriptStandardInstructions' + +type Props = { + type: 'standard' | 'popup' | 'bubble' +} + +export const ScriptInstructions = ({ type }: Props) => { + switch (type) { + case 'standard': { + return + } + case 'popup': { + return + } + case 'bubble': { + return + } + } +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/Script/instructions/ScriptPopupInstructions.tsx b/apps/builder/src/features/publish/components/embeds/modals/Script/instructions/ScriptPopupInstructions.tsx new file mode 100644 index 000000000..f4f467054 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/Script/instructions/ScriptPopupInstructions.tsx @@ -0,0 +1,34 @@ +import { CodeEditor } from '@/components/CodeEditor' +import { useTypebot } from '@/features/editor' +import { isCloudProdInstance } from '@/utils/helpers' +import { Stack, Text } from '@chakra-ui/react' +import { useState } from 'react' +import { env, getViewerUrl } from 'utils' +import { PopupSettings } from '../../../settings/PopupSettings' +import { parseInitPopupCode } from '../../../snippetParsers' +import { parseInlineScript } from '../../../snippetParsers/shared' + +export const ScriptPopupInstructions = () => { + const { typebot } = useTypebot() + const [inputValue, setInputValue] = useState() + + const scriptSnippet = parseInlineScript( + parseInitPopupCode({ + typebot: typebot?.publicId ?? '', + apiHost: isCloudProdInstance + ? undefined + : env('VIEWER_INTERNAL_URL') ?? getViewerUrl(), + autoShowDelay: inputValue, + }) + ) + + return ( + + setInputValue(settings.autoShowDelay)} + /> + Run this script to initialize the typebot: + + + ) +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/Script/instructions/ScriptStandardInstructions.tsx b/apps/builder/src/features/publish/components/embeds/modals/Script/instructions/ScriptStandardInstructions.tsx new file mode 100644 index 000000000..48ed4ea6e --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/Script/instructions/ScriptStandardInstructions.tsx @@ -0,0 +1,50 @@ +import { CodeEditor } from '@/components/CodeEditor' +import { useTypebot } from '@/features/editor' +import { isCloudProdInstance } from '@/utils/helpers' +import { Stack, Code, Text } from '@chakra-ui/react' +import { useState } from 'react' +import { env, getViewerUrl } from 'utils' +import { StandardSettings } from '../../../settings/StandardSettings' +import { parseInitStandardCode } from '../../../snippetParsers/standard' +import { parseStandardElementCode } from '../../Javascript/JavascriptStandardSnippet' +import { parseInlineScript } from '../../../snippetParsers/shared' + +export const ScriptStandardInstructions = () => { + const { typebot } = useTypebot() + const [inputValues, setInputValues] = useState<{ + heightLabel: string + widthLabel?: string + }>({ + heightLabel: '100%', + widthLabel: '100%', + }) + + const standardElementSnippet = parseStandardElementCode( + inputValues.widthLabel, + inputValues.heightLabel + ) + + const scriptSnippet = parseInlineScript( + parseInitStandardCode({ + typebot: typebot?.publicId ?? '', + apiHost: isCloudProdInstance + ? undefined + : env('VIEWER_INTERNAL_URL') ?? getViewerUrl(), + }) + ) + + return ( + + setInputValues({ ...settings })} + /> + + Make sure you have this typebot-standard element in your{' '} + {''}: + + + Then, run this script to initialize the typebot: + + + ) +} diff --git a/apps/builder/src/features/publish/components/embeds/snippetParsers/shared.ts b/apps/builder/src/features/publish/components/embeds/snippetParsers/shared.ts index 48bd9c8aa..40b46f4b4 100644 --- a/apps/builder/src/features/publish/components/embeds/snippetParsers/shared.ts +++ b/apps/builder/src/features/publish/components/embeds/snippetParsers/shared.ts @@ -1,4 +1,6 @@ import { BotProps } from '@typebot.io/js' +import parserBabel from 'prettier/parser-babel' +import prettier from 'prettier/standalone' import { isDefined } from 'utils' export const parseStringParam = (fieldName: string, fieldValue?: string) => @@ -30,3 +32,12 @@ export const parseReactBotProps = ({ typebot, apiHost }: BotProps) => { } export const typebotImportUrl = `https://cdn.jsdelivr.net/npm/@typebot.io/js@0.0.14/dist/web.js` + +export const parseInlineScript = (script: string) => + prettier.format( + `const typebotInitScript = document.createElement("script"); + typebotInitScript.type = "module"; + typebotInitScript.innerHTML = \`${script}\`; + document.body.append(typebotInitScript);`, + { parser: 'babel', plugins: [parserBabel] } + ) diff --git a/apps/docs/docs/embed/script.md b/apps/docs/docs/embed/script.md new file mode 100644 index 000000000..2892c6cd4 --- /dev/null +++ b/apps/docs/docs/embed/script.md @@ -0,0 +1,5 @@ +# Script + +The script embed option is useful only if you don't have access to the HTML tree of your application or if your website builder only allows you to inline script snippets. + +Otherwise, it's preferable to follow [HTML & Javascript](./html-javascript) embed instructions because the script snippets are just scripts that will inject the code from the HTML & Javascript embed method.