@ demoloop/ 反应
嵌入时的反应组件 脱摩洛普 AI在你的网站上演示.
倒入提供商,浮动CTA,内置按钮,或完整的相遇风格的英雄区段——全部接通同一张起牌,德莫洛普自己的脚本从你的页面上打开.
🔗 现场演示 – 反应-demo.demoloop.ai ——一页的每个组件.
安装
npm install @demoloop/react
同伴依赖性 : react >=18, (中文(简体) ), react-dom >=18。 。 。 . 没有其它运行时间解码 ——没有Tailwind,没有帧-动,没有图标库可以安装. 组件将自己的平地CSS和SVG图标装入 <head> 一次在第一舱, 并远离你的设计系统的方式.
使用量
将您的应用程序( 或任何子树) 包装到 DemoloopProvider,然后将任何按钮或部分放入其中.
import {
DemoloopProvider,
DemoloopFloatingOrb,
DemoloopButton,
DemoloopSection,
} from "@demoloop/react";
export default function Layout({ children }) {
return (
<DemoloopProvider agentId="agt_xxx">
{children}
<DemoloopFloatingOrb />
</DemoloopProvider>
);
}
就这样——没有CSS导入,没有Tailwind内容-路径配置,没有插件接线.
麦克风和你的 Permissions-Policy
演示从一个框架运行 https://app.demoloop.ai观众会跟它说话 所以这个相框需要麦克风 德莫洛普的脚本和 DemoloopEmbed 自行授权allow="microphone …" 说到框架 大部分网站不需要别的东西.
如果你的网站发送 Permissions-Policy 页眉(或 <meta http-equiv>提及 microphone,其许可名单必须包括: 你自己的出身和我们的出身数字 :
Permissions-Policy: microphone=(self "https://app.demoloop.ai")
self 是容易被忽略的部分, 并把它留下 打破演示沉默。 帧只获得一个功能,即它周围的页面也是允许的. 与 microphone=("https://app.demoloop.ai")您的页面不允许使用麦克风,所以里面的演示也不允许使用. 然后浏览器拒绝而不显示提示,而演示文稿告诉访问者页面不会让其询问(他们仍然可以在聊天中输入). microphone=() 挡住它一样的方式。 无标题 microphone 条目是罚款的:默认已允许您的页面, 而 iframe's allow 传之.
构成部分
DemoloopProvider
持有报价(演示可以运行的地方,以及为何)并曝光 open() 编号 : close() 给所有的孩子.
| Prop | 类型 | 默认 | 说明 |
|---|---|---|---|
agentId | string | —— 说 | 探员 每页一个特工 没有,触发器就变了 open() 什么也没做. |
baseUrl | string | https://app.demoloop.ai | 覆为自宿. 尾声 /d (0.x要求的)被忽略. |
copy | DemoloopCopyInput | 联合国 | SDK 制作的每个字符串, 作为一个打字对象 。 见 复制。 。 。 . |
theme | "light" | "dark" | "dark" | 看看SDK自己的触发器 开始卡跟随你的页面. |
lang | "en-US" | "es-ES" | string | —— 说 | 起步卡开通的语言(访客可以在那里更改). 没有它,你的页面 <html lang>。 。 。 . |
email | string | —— 说 | 当你的应用程序知道谁是访客时 这张卡不要求电子邮件。 未验证,如打字地址为. |
name | string | —— 说 | 他们的名字,相传 email (而只用它),所以经纪人从来不要. |
surfaces | ("call" | "in-app")[] | ["call"] | 演示可以运行的地方,以便提供. 见 表面和模式。 。 。 . |
modes | ("tour" | "help")[] | ["tour"] | 演示可能是为了提供它们. |
feature | string | —— 说 | 默认焦点特性 id: 巡演被缩小到该特性. |
onOpen | (info: DemoloopOpenInfo) => void | —— 说 | 每次触发器打开起动卡时都会拨打. 见 跟踪。 。 。 . |
loadScript | "mount" | "open" | "mount" | 当取出德摩洛普的脚本. "open" 只有在演示开始(或标签中已运行)时才加载,或作为 DemoloopFloatingOrb 装上,因为脚本画它。 见 一个脚本,每个演示。 。 。 . |
DemoloopFloatingOrb
浮起"AI aura" orb被钉入下角——一个发光的,有形态的梯度环绕着物剂avatar (à la 11Labs). 点击它打开了 Demoloop 的起步卡 .
德莫洛普的脚本绘制出来( D)window.Demoloop.orb),在你的页面上,除了起步卡和呼叫之外,在自己的一帧中. 组件本身不起作用;它告诉脚本应该是什么。 框架是一个单独的文档, 所以当您的应用程序有一个对话框或工作表打开时, Orb 继续工作: 模式 pointer-events: none,它的"点击外部关闭它",它的焦点陷阱从没有到达它,你的CSS也没有. 每页有1个圆形;第二个取而代之. 当开始卡或演示升起时,它会步出出道.
| Prop | 类型 | 默认 |
|---|---|---|
position | "right-bottom" | "left-bottom" | "right-bottom" |
label | string | "Talk to our AI" (单位:千美元)"Need help?" 当它只提供帮助时 |
content | "avatar" | "sparkles" | "orb" | "avatar" |
surfaces | ("call" | "in-app")[] | 提供者的 |
modes | ("tour" | "help")[] | 提供者的 |
feature | string | 提供者的 |
draggable | boolean | true |
persistPosition | boolean | true |
dismissible | boolean | true |
closeLabel | string | "Hide for now" |
bottom | number (px) (英语) | 0 |
访客在覆盖需要的东西时可以将球体移动到另一个下角: 被拖走,它跟随指针并定居在更近的角; 有键盘聚焦,左右箭头键移动. 拖放永远不会触发点击,因此演示只打开了真实的自带. 起步卡和演示会打开了Orb的一面,因此它们从未出现在另一角. position 只有它开始的地方。 游客选择的角落被记住在 localStorage,范围为代理人;设置 persistPosition={false} 在下一页装入时忘记它,或者 draggable={false} 将矿石固定在原地.
在悬浮或焦点上的球体上会出现一个小的相近按钮(在触摸上总是可见,没有悬浮的地方). 点击它隐藏矿石 只用于浏览器会话 ——辞退生活 sessionStorage,放入代理和角落,所以矿石又回到了访客的下一次访问或一个新的标签。 过 dismissible={false} 使矿石无法关闭 closeLabel 来翻译按钮的可访问标签.
这个 label 显示为悬浮/聚焦的工具提示。 您的 CSS 无法移动它, 因此, 如果您的应用程序在屏幕下方有条栏( 电话标签栏) , 请通过它的高度 bottom 球体在下面有那么多空间 设定 content="orb" 以没有象形文字或图标的纯活体表示。 设定 modes={["help"]} 或通过 feature d 开设目标单一特质旅游——见 表面和模式。 。 。 .
DemoloopButton
内置按钮 。 接受标准 <button> 属性加 children, (中文(简体) ), className, (中文(简体) ), showIcon没有 children 写着 copy.inlineButton.label (单位:千美元)"See it live") (中文(简体) ).
DemoloopSection
满出血的部分 模糊的相遇风格的背景。 将居中标题+字幕作为前置组成.
| Prop | 类型 | 默认 |
|---|---|---|
title | string | "You ask. It shows you." |
subtitle | string | "An AI agent uses the product while you watch. Pick a feature, change your mind, interrupt anytime. It adapts." |
buttonLabel | string | "Start the demo" |
DemoloopLiveSection
模拟真实的现场通话视图作为营销部分. 头条/周围的复制本故意不属于这个组件——用自己部分的标记来包装.
| Prop | 类型 | 默认 |
|---|---|---|
buttonLabel | string | "Join the live demo" |
chromeLabel | string | "live demo" |
agentName | string | "Demo agent" |
DemoloopPreviewSection
居中卡有浏览器-色相条,代理可视化,标记行,并有"开始演示"按钮. 头条/周围的复制本故意不属于这个组件——用自己部分的标记来包装.
| Prop | 类型 | 默认 |
|---|---|---|
displayName | string | "Your demo agent" |
tagline | string | "Will share a website and talk with you live" |
buttonLabel | string | "Start the demo" |
chromeLabel | string | "Live demo session" |
showChrome | boolean | true |
DemoloopEmbed
代理主机启动页面的内置活-demo iframe——没有覆盖. 当您希望演示直接生活在一页而不是CTA之后时使用此功能.
| Prop | 类型 | 默认 | 说明 |
|---|---|---|---|
aspectRatio | string | "16/10" | CSS的外观比. 忽略当 height 已经设定. |
height | string | number | —— 说 | 显高(数字为px). 有用于出满出血入地. |
framed | boolean | true | 将iframe包装在有环和阴影的圆形卡片中. |
title | string | "Demo" | Iframe无障碍标题. |
直到提供商拥有 agentId 牌是空的,没有Iframe。 iframe的url载 iframe=1显示它嵌入了演示, 从而让自己处于边缘.
<DemoloopProvider agentId="agt_xxx">
<DemoloopEmbed aspectRatio="16/9" />
</DemoloopProvider>
useDemoloop()
虎克回来了 { agentId, url, avatarUrl, theme, surfaces, modes, open, close }用它来建立自己的触发器. url 是代理主机的起始页面作为链接(什么 DemoloopEmbed 画框, 和什么平地 <a> 可以指向: 它携带 lang, (中文(简体) ), email, (中文(简体) ), theme 财务报告和审定财务报表 feature 当设置,和 mode=support 当第一个模式是 "help"。 。 。 .
open(options?)数字 :
- 为提供商打开 Demoloop 的起步卡
surfaces财务报告和审定财务报表modes,除非options点击时覆盖它们。 它只问访客 留下什么 直接进去 当没有选择时:一个表面、一个模式和一个已知模式email立即启动呼叫. options实值{ source?: string; surfaces?: DemoloopSurface[]; modes?: DemoloopMode[]; feature?: string }。 。 。 .source交给供应商onOpen回调( 默认为"custom",参见 跟踪) (中文(简体) ).- 回返a
Promise<boolean>数字 :true一旦打开 交给页面的脚本,false当它不能是(没有代理,脚本不会加载,或者close()先来 它从不拒绝,因此一个无视它的触发器是好的;习惯可以在它没有打开的时候说出. - 没有
agentId参数: Demoloop 的脚本每页运行一个代理 .
close() 将卡片取出,或演示会从页面取出. 当用户也签名时调用它:卸载提供者不会故意结束运行的演示,因为您的应用程序本身的导航会卸载它.
一个脚本,每个演示
每个演示,一个在你的页面上的呼叫, 或代理 在访客自己的标签,打开一个脚本: Demoloop's https://app.demoloop.ai/embed/v1.js。 。 。 . DemoloopProvider 装入( 从您的 baseUrl'来源,每页一次)和每个触发器都叫它 window.Demoloop.open({ surfaces, modes, feature, lang }),在页面上显示Demoloop的起步卡。 SDK没有自己的模式.
- 每页挂载提供者 操作程序, 不仅仅是在触发器所在的地方: 脚本是在重新装入或全页导航后带回运行演示.
loadScript="open"将脚本从您的页面中保存下来,直到它被使用:它装入了第一个打开的脚本,或者当演示文稿已经在此标签中运行时挂载(所以重新装入仍然会重新装入它). Demoloop的负载没有给从未开始演示的访客,除非页面有一个DemoloopFloatingOrb:脚本所画出来,所以作为圆形上架来加载. 取舍:德莫洛普的安装检查("部署"页面)只听到启动一个的浏览器的声音.email财务报告和审定财务报表name被传递作为谁的访客 (单位:千美元)name只有一个email因此,他们也没有被要求。 当打开的脚本到达时,它们会被读取,所以在装入时到达的道具仍然数起. 没有email无论您的应用程序直接告诉了脚本(window.Demoloop.identify留下来.- 每页一个特工. 脚本运行它所装入的第一个代理;另一个代理的提供者不切换(控制台说一次如此).
onOpen一旦打开脚本就开火,从不为无法装入的脚本.- 点击一次即时得到回答,甚至在脚本上架之前. 自 2.2.0起,在脚本仍在加载时打开(点击一页第一秒,或慢接上),即刻取出脚本自己的"接通""接通"接通"接通"接通"接通"接通"接通"接通"接通"接通"接通"接通"接通"接通"接通"接通"接通"接通"接通"接通"接通"接通"接通"接通"接通"接通"接通"的接通"接通"接通"接通"接通"接通"的接通"接通"接通"接通"接通"接通"接通"接通"接通"接通"接通"接通"接"接"接接接"接"接"接"接"接"接"接"接"接"接通"接接"接接接接"接"接"接"接"接接接"接"接"接接接接"接"接"接"接"接"接"接接接"接"接"接"接 剧本一出就在同一地段接手. 如果它从不装货,瓷砖 说再次尝试。 超过一个脚本,SDK画出一文不值:脚本显示出其瓦片本身.
内容-安全政策
如果您的页面发送 CSP, 它必须允许 https://app.demoloop.ai 内 script-src (脚本), frame-src (起始卡和演示为帧)和 connect-src (剧本向它汇报). 被屏蔽的脚本(没有这些脚本的CSP,或广告屏蔽器)向访客显示"Couldn't 加载"的平板并记录出错; 再试一次,或者下一个打开, 重新装入它.
script-src 'self' https://app.demoloop.ai;
frame-src 'self' https://app.demoloop.ai;
connect-src 'self' https://app.demoloop.ai;
在您已有的指示中添加这些来源; 上面的行仅显示 Demoloop 部分 。 麦克风需要.. Permissions-Policy 说明 上文。 。 。 .
表面和模式
一个开放的提供两件事, 既设定在提供者上, 也设定在每个触发器或每点击一次的可压倒性:
surfaces: 演示运行的地方."call"(默认):代理在您的演示账户上共享屏幕, 在您的页面上调用 。 到处跑."in-app": 访客的经纪人 自己的登录标签在他们真实的账户上 页面上有一个小的呼叫部件 只有在代理程序启动时,你的应用程序地址在Demoloop(确切来源)中列出,https://app.example.com; 列出每个子域。 在其他地方,它被排除在游客提供的东西之外。 特工按照访客的意愿操作你的应用程序,所以它可以做任何他们能做的事情:它可能不会做的是设置在特工在德莫洛普的护栏上.
modes:演示是用来做什么的."tour"(默认):产品被引导行走."help": 反应性服务台,其中经纪人回答访客的问题而不是进行巡演.
数个值按给出的顺序给出,卡会要求访客选择. 一个表面,一个模式和一个 email 所以马上打个电话.
// A support bubble for the signed-in app
<DemoloopProvider agentId="agt_xxx" surfaces={["in-app"]} modes={["help"]} email={user.email} name={user.name}>
{children}
<DemoloopFloatingOrb /> {/* label defaults to "Need help?" */}
</DemoloopProvider>
每个触发器,超过提供者的:
<DemoloopProvider agentId="agt_xxx">
{children}
{/* A targeted single-feature tour */}
<DemoloopFloatingOrb feature="wbf_xxx" label="See this feature" />
</DemoloopProvider>
// Your own trigger, opting into a mode for this click
const { open } = useDemoloop();
<button onClick={() => open({ source: "pricing_help", modes: ["help"] })}>
Ask a question
</button>
A 类 feature 缩窄 a 旅游 到一个功能(或一页):它是Demolooop中一个功能的ID,完全适合您直接投入电子邮件的链接。 帮助会话忽略了它.
复制
SDK使生命在一个被打入的物体中的每一个字符串, DemoloopCopy子集为 copy 提供方,其余为英文.
import { DemoloopProvider, type DemoloopCopyInput } from "@demoloop/react";
const es: DemoloopCopyInput = {
floatingOrb: { label: "Habla con la IA", moveHint: "muévelo con las flechas" },
};
<DemoloopProvider agentId="agt_xxx" copy={es}>
这些团体是: floatingOrb, (中文(简体) ), inlineButton, (中文(简体) ), section, (中文(简体) ), liveSection, (中文(简体) ), previewSection 财务报告和审定财务报表 embed。开始卡和呼叫是Demoloop自己的,用卡打开的语言(lang故他们绝没有团体。 因为类型是关闭的,所以一个翻译是作为整体输入的 DemoloopCopy 是编译器的检查之一: 你没有翻译的每个字符串都是你看到的错误, 而不是你在生产中发现的流出英文单词 。 包括新版本添加的字符串,所以升级后会告诉你需要翻译什么:
import type { DemoloopCopy } from "@demoloop/react";
// Every group, every key. Leave one out and this does not compile.
const es: DemoloopCopy = {
floatingOrb: { label: "Habla con la IA", /* … */ },
// …
};
传播 defaultCopy 输入( e){ ...defaultCopy, floatingOrb: { ...defaultCopy.floatingOrb, close: "Cerrar" } })用英文填满每个洞并开关检查. 故意部分翻译也行 DemoloopCopyInput 已经覆盖.
单个组件仍然取自自己的标签道具,而那些为那一个实例而取胜的——一个页面上有不同标签的两个内在按钮是排版决定,而不是语言. useDemoloopCopy() 返回已解析的副本,如果您正在构建自己的触发器.
跟踪
DemoloopProvider 接受一个 onOpen 调回每次打开启动卡时都会点火, 不管它打开了什么: SDK的触发器之一, Orb(点击Demolooop的脚本处理本身), 或是你自己的 open()德莫洛普的剧本以一个 demoloop:open 活动 window (单位:千美元){ source, surfaces, modes, feature }),以及 onOpen 因此,使用脚本标记的页面可以直接为同一事件收听。 它收到一个 { source, agentId, surfaces, modes, feature } a. 信息对象(按顺序提供); agentId 是该页脚本实际运行的代理),所以可以在一地给分析器接线:
<DemoloopProvider
agentId="agt_xxx"
onOpen={({ source, agentId }) => {
posthog.capture("demo_opened", { source, agentId });
}}
>
{children}
</DemoloopProvider>
内建 source 值 :
| 构成部分 | source |
|---|---|
DemoloopFloatingOrb | "floating_orb" |
DemoloopLiveSection | "live_section" |
DemoloopSection | "section" |
DemoloopPreviewSection | "preview_section" |
DemoloopButton | "inline_button" |
useDemoloop().open() | "custom" |
打电话时 open() 直接从自己的按钮中,您可以提供更具体的源 :
const { open } = useDemoloop();
<button onClick={() => open({ source: "hero_button" })}>
Live demo
</button>
造型和主题
所有样式都是简单的 CSS 范围 dl-* 分类名称,注入 <head> 一次在第一舱。 要自定义, 超越您样式表中的类规则 —— 它们有低特异性( 单一类选择器) , 所以任何在我们注入后加载的规则 <style data-demoloop> 会赢的 通过 [data-theme="light"] 编号 : [data-theme="dark"]。 。 。 .
自0.8.0起 SDK的动画没有任何内容 浏览器必须重新油漆或铺出每个相框 没有任何动画可以让人看到它:
- 活体部分的CTA发光和相接控制器的发光被剪去,模糊的药丸被静态的圆锥旋转入内
.dl-aura-spin两个在CTA, 一个在联合控制。 他们曾经转身@property内侧角度conic-gradient()和形态border-radius,它重新撕裂和 重击他们60次一秒钟。 接合的光线只在徘徊时旋转. - Faux 光标为移动的全尺寸图层
transform(它用来动画top编号 :left) (中文(简体) ). - 说话的瓷砖的脉搏是
::after其不透明脉冲(用于激活)box-shadow) (中文(简体) ). - CTA和加盟按钮的填充更暗而没有
backdrop-filter。 。 。 . - 活体部分自己标出
data-dl-offscreen既已卷起,而其中的一切事物都静止了. - 减少运动停止了每个循环动画.
在一页闲置中,直播部分从每10人约2人的主线行走到屏幕上0.2人,没有屏幕关闭. 钩子不再做任何事情: --dl-aura-angle (过去), .dl-live-section__cta-aura::before (移动到 .dl-aura-spin--counter页:1 background 打开 .dl-live-section__cta-aura 或者说 .dl-tile__join-aura (颜色在 .dl-aura-spin 现在,在一个 .dl-aura-clip) (中文(简体) ). 说话的瓷砖的戒指被画入瓷砖内部,因此失去了外光环. 在减速运动下,平和脉冲光圈被隐藏起来,而不是作为盘子休息.
从1.x升级
2.0 将浮出水面的 Orb 交给 Demoloop 的脚本, 这样它就可以在您的应用程序自己的对话框上工作(参见 DemoloopFloatingOrb) (中文(简体) ).
DemoloopFloatingButton不见了,带有copy.floatingButton, (中文(简体) ),DemoloopPosition,则"floating_button"来源及其来源.dl-floating-cta*课程。 使用DemoloopFloatingOrb,或DemoloopButton编号 :useDemoloop().open()在自己的布局中.DemoloopFloatingOrb在你的页面上画什么. 内容className道具和电线.dl-floating-orb*课程已去:Orb住在Demoloop的相框中,CSS页面无法访问. 已关闭的 Orb 保持关闭( 相同的存储密钥) .- 球体会到下角 而不是留在投放的地方, 而卡和演示打开在它的侧面。 被拖过一页的一轴球体 回到了它被拖去的那一边.
onOpen从剧本中报告demoloop:open事件,所以它为 Orb 自己的点击点开火。 提供商随时告诉剧本 访客是谁email或者说name更改,不仅仅是点击.- 矿石需要脚本. 搭载的矿石,不管怎样
loadScript说,和一个页面的 CSP 屏蔽脚本显示没有矿石.
从 0.x 升级
1.0去掉SDK自己的模式:每个演示都通过德莫洛普的脚本及其起步卡打开(参见: 一个脚本,每个演示) (中文(简体) ). 大多数嵌入体只需要下面的重命名.
| 0.x (中文(简体) ) | 1.0 国家 |
|---|---|
surface="hosted" (默认) | surfaces={["call"]} (默认) |
surface="in-app" | surfaces={["in-app"]} |
mode="demo" | modes={["tour"]} (默认) |
mode="support" | modes={["help"]} |
open(agentId, { source, feature, mode }) | open({ source, feature, modes }): 每页有一个代理, 所以没有 agentId 参数 |
useDemoloop().surface 编号 : .mode | useDemoloop().surfaces 编号 : .modes |
onOpen({ source, agentId, mode }) | onOpen({ source, agentId, surfaces, modes, feature }) |
baseUrl="https://app.demoloop.ai/d" | baseUrl="https://app.demoloop.ai" (旧值仍然有效) |
- 模式不见了,用它的所有东西:
allowFullscreen, (中文(简体) ),confirmFullscreen, (中文(简体) ),confirmClose, (中文(简体) ),onClose, (中文(简体) ),copy.modal,则.dl-modal-*分类和data-dl-modal-open属性。 Demolooop的卡片用自己设定的语言进行加载、关闭和离开lang。 。 。 . DemoloopEmbed编辑url从useDemoloop()仍然帧 / 链接代理主机的启动页面 .- 你们的CSP 必须允许
https://app.demoloop.ai内script-src, (中文(简体) ),frame-src财务报告和审定财务报表connect-src: 只装有主机的 0.x 仅需嵌入frame-src。 。 。 . loadScript财务报告和审定财务报表name现在适用于每个演示, 不只是应用中.
许可证
麻省理工学院