diff --git a/web/app/(shareLayout)/chatbot/[token]/page.tsx b/web/app/(shareLayout)/chatbot/[token]/page.tsx new file mode 100644 index 0000000000..8aa182893a --- /dev/null +++ b/web/app/(shareLayout)/chatbot/[token]/page.tsx @@ -0,0 +1,13 @@ +import type { FC } from 'react' +import React from 'react' + +import type { IMainProps } from '@/app/components/share/chat' +import Main from '@/app/components/share/chatbot' + +const Chatbot: FC = () => { + return ( +
+ ) +} + +export default React.memo(Chatbot) diff --git a/web/app/components/app/chat/index.tsx b/web/app/components/app/chat/index.tsx index f67805cbf4..9dcf37399d 100644 --- a/web/app/components/app/chat/index.tsx +++ b/web/app/components/app/chat/index.tsx @@ -473,7 +473,7 @@ const Chat: FC = ({ } } - const haneleKeyDown = (e: any) => { + const handleKeyDown = (e: any) => { isUseInputMethod.current = e.nativeEvent.isComposing if (e.code === 'Enter' && !e.shiftKey) { setQuery(query.replace(/\n$/, '')) @@ -573,7 +573,7 @@ const Chat: FC = ({ value={query} onChange={handleContentChange} onKeyUp={handleKeyUp} - onKeyDown={haneleKeyDown} + onKeyDown={handleKeyDown} minHeight={48} autoFocus controlFocus={controlFocus} diff --git a/web/app/components/app/overview/appCard.tsx b/web/app/components/app/overview/appCard.tsx index f6358d27df..0a2f8e73f9 100644 --- a/web/app/components/app/overview/appCard.tsx +++ b/web/app/components/app/overview/appCard.tsx @@ -1,4 +1,5 @@ 'use client' +import type { FC } from 'react' import React, { useState } from 'react' import { Cog8ToothIcon, @@ -11,6 +12,7 @@ import { usePathname, useRouter } from 'next/navigation' import { useTranslation } from 'react-i18next' import SettingsModal from './settings' import ShareLink from './share-link' +import EmbeddedModal from './embedded' import CustomizeModal from './customize' import Tooltip from '@/app/components/base/tooltip' import AppBasic, { randomString } from '@/app/components/app-sidebar/basic' @@ -18,6 +20,8 @@ import Button from '@/app/components/base/button' import Tag from '@/app/components/base/tag' import Switch from '@/app/components/base/switch' import type { AppDetailResponse } from '@/models/app' +import './style.css' +import { AppType } from '@/types/app' export type IAppCardProps = { className?: string @@ -29,6 +33,10 @@ export type IAppCardProps = { onGenerateCode?: () => Promise } +const EmbedIcon: FC<{ className?: string }> = ({ className = '' }) => { + return
+} + function AppCard({ appInfo, cardType = 'app', @@ -42,6 +50,7 @@ function AppCard({ const pathname = usePathname() const [showSettingsModal, setShowSettingsModal] = useState(false) const [showShareModal, setShowShareModal] = useState(false) + const [showEmbedded, setShowEmbedded] = useState(false) const [showCustomizeModal, setShowCustomizeModal] = useState(false) const { t } = useTranslation() @@ -49,8 +58,9 @@ function AppCard({ webapp: [ { opName: t('appOverview.overview.appInfo.preview'), opIcon: RocketLaunchIcon }, { opName: t('appOverview.overview.appInfo.share.entry'), opIcon: ShareIcon }, + appInfo.mode === AppType.chat ? { opName: t('appOverview.overview.appInfo.embedded.entry'), opIcon: EmbedIcon } : false, { opName: t('appOverview.overview.appInfo.settings.entry'), opIcon: Cog8ToothIcon }, - ], + ].filter(item => !!item), api: [{ opName: t('appOverview.overview.apiInfo.doc'), opIcon: DocumentTextIcon }], app: [], } @@ -80,6 +90,10 @@ function AppCard({ return () => { setShowSettingsModal(true) } + case t('appOverview.overview.appInfo.embedded.entry'): + return () => { + setShowEmbedded(true) + } default: // jump to page develop return () => { @@ -139,20 +153,20 @@ function AppCard({ key={op.opName} onClick={genClickFuncByName(op.opName)} disabled={ - [t('appOverview.overview.appInfo.preview'), t('appOverview.overview.appInfo.share.entry')].includes(op.opName) && !runningStatus + [t('appOverview.overview.appInfo.preview'), t('appOverview.overview.appInfo.share.entry'), t('appOverview.overview.appInfo.embedded.entry')].includes(op.opName) && !runningStatus } >
- + {op.opName}
@@ -193,6 +207,12 @@ function AppCard({ onClose={() => setShowSettingsModal(false)} onSave={onSaveSiteConfig} /> + setShowEmbedded(false)} + appBaseUrl={app_base_url} + accessToken={access_token} + /> + + diff --git a/web/app/components/app/overview/assets/iframe-option.svg b/web/app/components/app/overview/assets/iframe-option.svg new file mode 100644 index 0000000000..d9ffc024aa --- /dev/null +++ b/web/app/components/app/overview/assets/iframe-option.svg @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/web/app/components/app/overview/assets/scripts-option.svg b/web/app/components/app/overview/assets/scripts-option.svg new file mode 100644 index 0000000000..fee40407b6 --- /dev/null +++ b/web/app/components/app/overview/assets/scripts-option.svg @@ -0,0 +1,160 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/web/app/components/app/overview/embedded/index.tsx b/web/app/components/app/overview/embedded/index.tsx new file mode 100644 index 0000000000..225ab9667f --- /dev/null +++ b/web/app/components/app/overview/embedded/index.tsx @@ -0,0 +1,111 @@ +import React, { useState } from 'react' +import { useTranslation } from 'react-i18next' +import cn from 'classnames' +import style from './style.module.css' +import Modal from '@/app/components/base/modal' +import useCopyToClipboard from '@/hooks/use-copy-to-clipboard' +import copyStyle from '@/app/components/app/chat/copy-btn/style.module.css' +import Tooltip from '@/app/components/base/tooltip' +import { useAppContext } from '@/context/app-context' + +// const isDevelopment = process.env.NODE_ENV === 'development' + +type Props = { + isShow: boolean + onClose: () => void + accessToken: string + appBaseUrl: string +} + +const OPTION_MAP = { + iframe: { + getContent: (url: string, token: string) => + ``, + }, + scripts: { + getContent: (url: string, token: string, isTestEnv?: boolean) => + ` +`, + }, +} +const prefixEmbedded = 'appOverview.overview.appInfo.embedded' + +type Option = keyof typeof OPTION_MAP + +const Embedded = ({ isShow, onClose, appBaseUrl, accessToken }: Props) => { + const { t } = useTranslation() + const [option, setOption] = useState