@ fern-api/ search-widget (英语)
可嵌入到任何 React 应用程序的 AI 驱动文档部件 :
SearchModal——关键词搜索+在中模式覆盖中询问AI.AskAiChat——只问AI,作为一角的浮动聊天卡(没有关键词搜索UI).AskAiChat.Root编号 :AskAiChat.Trigger编号 :AskAiChat.Panel- 和扳机同声聊天 在下拉菜单、弹出式或命令调色板内设置触发器.
他们都通过Fern Docs网站 指向 domain 道具 (英语).
安装
npm install @fern-api/search-widget react react-dom
或与平按:
pnpm add @fern-api/search-widget react react-dom
同伴依赖关系
此软件包需要 仅反应 19数字 :
{
"react": "^19",
"react-dom": "^19"
}
所有其他依赖关系都捆绑在一起 - 包括国家管理(Jotai)、数据采集(SWR)和AI SDK库。 你只需要提供19号反应.
使用量
基本设置
首先,导入应用程序中的组件和样式:
import { SearchModal } from '@fern-api/search-widget';
import '@fern-api/search-widget/styles';
然后使用搜索模式组件 :
function App() {
return (
<div>
<SearchModal
domain="https://your-docs-site.com"
lang="en"
>
🔍 Open Search
</SearchModal>
</div>
);
}
Next.js 和其他服务器移交框架
这两个组件都是客户端组件——它们使用React上下文,状态和效果. 在那个
下一个.js App路由器,这意味着文件让他们需要 "use client" 指令 :
'use client';
import { AskAiChat } from '@fern-api/search-widget';
import '@fern-api/search-widget/styles';
export function AskAi() {
return <AskAiChat domain="https://your-docs-site.com" />;
}
没有它,你得到 TypeError: createContext only works in Client Components平地
import 否则,所有需要: 捆绑在服务器上是安全的,所以
next/dynamic 财务报告和审定财务报表 ssr: false 为: 没有 所需经费.
Pages 路由器和非下一个设置( Vite, CRA, Remix) 不需要特别的设置; 部件是 挂载客户端 .
道具
这个 SearchModal 组件接受下列道具:
必需的道具
domain(字符串):要搜索的文档域(例如,"https://buildwithfern.com/learn"(中文(简体) )
可选道具
支持所有标准的 HTML 按钮道具,并将转发到触发按钮:
lang(字符串):搜索界面的语言代码(例如,"en") (中文(简体) ). 默认为"en"icon(React.ReactNode):在按钮中显示的图标元素className: 按钮的额外 CSS 类style: 按钮的内置样式onClick: 额外的点击处理器(打开模式前运行)disabled: 禁用按钮children:按钮内容(文本,图标等)
按钮样式
您可以在三种方式中样式触发按钮:
1. 内含样式
<SearchModal
domain="https://docs.example.com"
lang="en"
style={{
padding: '0.75rem 1.5rem',
fontSize: '1rem',
background: '#2563eb',
color: 'white',
border: 'none',
borderRadius: '6px',
cursor: 'pointer'
}}
>
Search Documentation
</SearchModal>
2. CSS类
<SearchModal
domain="https://docs.example.com"
lang="en"
className="my-search-button"
>
Search
</SearchModal>
.my-search-button {
padding: 0.75rem 1.5rem;
background: #2563eb;
color: white;
border: none;
border-radius: 6px;
}
3. 使用默认类别
按钮有一个 fern-search-button 可以瞄准的类 :
.fern-search-button {
padding: 0.75rem 1.5rem;
background: #2563eb;
color: white;
}
询问 AI 聊天( 不搜索 UI)
AskAiChat 向您提供来自Fern Docs网站的“问AI”体验——流出答案,
引用,新聊天——没有关键字搜索 UI,作为浮卡锁定到一角
观景台。 下面 md 断点(768px)成为了底部抽屉.
import { AskAiChat } from '@fern-api/search-widget';
import '@fern-api/search-widget/styles';
function App() {
return <AskAiChat domain="https://buildwithfern.com/learn" />;
}
这就是整个集成: 它自己搭载发射器按钮, 被固定在右下方.
因为答案存在于Docs网站 而不是你的应用 引用指向您
docs 域并打开新标签(target="_blank" rel="noreferrer"所以读者从不
离开你的页面.
在下一个.js App路由器上添加 "use client" 转到文件 — 见
Next.js 和其他服务器移交框架。 。 。 .
道具
AskAiChat 使用与 SearchModal — 所有标准的 HTML 按钮道具都是
转接到触发按钮上——加上它自己的,下面.
必需的道具
domain(字符串):要询问的docs域名(例如:."https://buildwithfern.com/learn"(中文(简体) )
可选道具
trigger(单位:千美元)"floating" | "inline" | "none":"floating"(默认)使样式 发射器被钉在一角."inline"无论你放在哪里,都会变成一个没有风格的按钮."none"没有触发器, 用于打开您自己的用户界面的聊天—— 看 触发模式对于下拉菜单或弹出式菜单中的触发器,使用AskAiChat.Root编号 :AskAiChat.Trigger编号 :AskAiChat.Panelopen(boolean):自行驱动聊天,而不是让部件拥有自己的状态. 过onOpenChange并肩作战——看 从你的UI打开它defaultOpen(boolean):控制状态时的初始开放状态. 被忽略过一次open被通过onOpenChange(单位:千美元)(open: boolean) => void:每当聊天要打开或关闭时,就起火, 从任何来源——触发器,卡的关闭按钮"逃出",或cmd编号 :ctrl+ 键/调用 无论是控制模式还是控制模式placement(单位:千美元)"bottom-right" | "bottom-left" | "top-right" | "top-left": 哪个角 发射装置和卡片锁定 默认为"bottom-right"draggableCard(boolean):参观者是否可以将卡片拖入景点的任何地方 标题或由随其移动的发射器;placement仍然决定从哪里开始, 也不能被拖出屏幕,卡片打开 在发射器的任意一边有空间 对于它。 发射装置的闪光在指针被接住后会变成握住(或者,与data-drag-hint="tab"在发射器上,一个抓取标签滑出 它的一面在徘徊。 默认为true。下面的抽屉md永远无法拖动theme(单位:千美元)"inherit" | "light" | "dark" | "auto":颜色方案. 默认为"inherit", (中文(简体) ), 它跟随您自己的网站主题——见 黑暗模式accentColor(字符串):你的品牌颜色,任何CSS颜色. 部件中性默认值 灰色——见 中间颜色lang(字符串):UI语言代码. 默认为"en"searchLocale(字符串):覆盖用于检索的地块. 默认为langicon(React.ReactNode):替换触发器的闪光图标children: 替换触发器Ask AI标签——例如:.Search docs
一号 AskAiChat 每页。 有两起事件表明其发射器具有相同的识别码和识别码
同一个角落,所以他们堆: 顶部一个吞下每一个点击,另一个变成
不可通达. 如果在一个以上的地方需要触发器——导航栏、帮助菜单和脚
第 11 条 AskAiChat.Root 一个 AskAiChat.Panel 也一样多
AskAiChat.Trigger随你便,其中 最多一个 variant="floating"编辑
发射装置被固定在取景口角,并携带固定的id,因此第二发无处可去;
内线触发器没有这种限制。 见
下拉菜单、弹出或命令调色板中的触发器。 。 。 .
触发模式
trigger="floating" (默认) ——一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作一作
用于AI助手. 无样式; 覆盖标签和图标 children
财务报告和审定财务报表 icon,或者完全通过 #fern-ask-ai-launcher。 。 。 .
trigger="inline" 在自己的导航栏上放"问AI"按钮 内容与
发射装置——闪光和一发 Ask AI 标签——a <button> 携带
fern-ask-ai-button 班级,无论你把它放在树上,同样的安排
SearchModal 用于 fern-search-button数字 :
<AskAiChat domain="https://docs.example.com" trigger="inline" className="my-nav-button" />
与发射器不同的是,它没有被涂上-没有表面、边框、颜色或字体,所以它
以导航的造型; 闪光大小 1em 并绘制
currentColor (或你的电话) accentColor,如果设置)。 重塑它通过 className, (中文(简体) ), style,或
,并替换其中一半:
<AskAiChat domain="https://docs.example.com" trigger="inline">
Search docs
</AskAiChat>
那张牌还挂着 placement 在此模式下; 它不是用来清除
发射装置.
trigger="none" 没有触发器,只是聊天,打开通过 open 编号 :
onOpenChange。见 从你的UI打开它。 。 。 .
下拉菜单、弹出或命令调色板中的触发器
别装了 AskAiChat 在菜单中. 它看起来应该起作用, 它使什么
所有情况:
{/* Broken. */}
<DropdownMenu.Item asChild>
<AskAiChat domain="https://docs.example.com" trigger="inline" />
</DropdownMenu.Item>
选择项目会关闭菜单, Radix —— 如无头 UI, 以及大多数菜单库 —— 关闭时卸载其内容。 这需要整个部件与它,包括聊天,之前 {\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}聊天会让大家觉得 没有出现,没有错误, 这确实令人困惑 失败.
继续打开菜单( N)onSelect={(event) => event.preventDefault()}用它来交易
更糟糕的是: Radix 的模式菜单集 pointer-events: none 打开 <body> 当它打开,和
聊天, 已传送到 <body>,继承它。 纸牌画,看起来正确,忽略了每次点击
还有按键.
固定是让菜单关闭,将部件一分为二——菜单内的触发器,在哪里 解载是好的, 和它的外边的聊天, 在那里没有什么可以解载它:
import { AskAiChat } from "@fern-api/search-widget";
import "@fern-api/search-widget/styles";
function HelpMenu() {
return (
<AskAiChat.Root domain="https://docs.example.com">
<DropdownMenu.Root>
<DropdownMenu.Trigger>Help</DropdownMenu.Trigger>
<DropdownMenu.Portal>
<DropdownMenu.Content>
<DropdownMenu.Item>Contact support</DropdownMenu.Item>
<DropdownMenu.Item asChild>
{/* Fern's button, as the menu item */}
<AskAiChat.Trigger />
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu.Root>
{/* Outside the menu — this is the part that matters */}
<AskAiChat.Panel />
</AskAiChat.Root>
);
}
AskAiChat.Root保存状态、搜索证书和主题,不设置 DOM 自己来的 这需要domain, (中文(简体) ),lang, (中文(简体) ),searchLocale, (中文(简体) ),placement, (中文(简体) ),theme, (中文(简体) ),accentColor,则 不变open编号 :defaultOpen编号 :onOpenChange作为AskAiChat围绕菜单和 显示面板.AskAiChat.Trigger是真实的<button>这样asChild工作和 Radix 保留其菜单项 键盘行为。 它携带本地化Ask AI标签,重音填充 闪闪发光,aria-haspopup="dialog"财务报告和审定财务报表aria-controls-你不需要再繁殖.variant="floating"给您一个转角发射器 而不是一个未涂的按钮;children财务报告和审定财务报表icon替换标签和格言.AskAiChat.Panel这是聊天。 每根一个,在菜单之外 它警告我们 如果你忘记它,控制台, 因为一个没有面板的触发器 否则会沉默地死去.
这与模式菜单和非模式菜单相同,同样的形状覆盖了弹出器,命令 调色板,或者导航杆倒下.
从你的UI打开它
如果触发器不是按钮——链接、列表行、调色板条目——设置
trigger="none" 打开 AskAiChat 驱动 open 自己说:
import { AskAiChat } from "@fern-api/search-widget";
const [askAiOpen, setAskAiOpen] = useState(false);
<a href="#" onClick={() => setAskAiOpen(true)}>
<AskAiChat.Sparkles /> Still stuck? Ask AI
</a>
<AskAiChat
domain="https://docs.example.com"
trigger="none"
open={askAiOpen}
onOpenChange={setAskAiOpen}
/>
open 被控制在通常的反应意义中: 部件让你得到的东西, 所以
onOpenChange 必须被接回你的状态 或聊天的自控器,它的关闭按钮,
越狱, cmd编号 :ctrl + 键 / - 似乎不会做什么。 不通过道具和部件
管理自己的国家; onOpenChange 做个通知钩. ref 留下来 null, (中文(简体) ),
既然我们没有扣子可以附加 触发器的无障碍性是你的:
aria-haspopup="dialog"附加时 aria-expanded 如果它是一个按钮.
AskAiChat.Sparkles 是内置触发器和卡片头的弹出,所以你自己的触发器
读作同样的支付。 大小为 1em 并取出其颜色
需要导入的样式表( 部件无论如何都需要) 才能有大小 .
自定义外观
所有视觉都是CSS的自定义属性,遵循与CSS相同的惯例
--fern-search-dialog-z-index。将其中任何一种设置在父母身上(或 :root:
| 变量 | 控件 | 默认 |
|---|---|---|
--fern-ask-ai-z-index | 堆放发射器、卡片和抽屉 | 2147483646 |
--fern-ask-ai-offset | 距离取景口角 | 20px |
--fern-ask-ai-width | 卡片宽度 | 400px |
--fern-ask-ai-height | 牌高( 封顶到取景口) | min(640px, 70dvh) |
--fern-ask-ai-expanded-width | 扩展时的卡宽度 | 480px |
--fern-ask-ai-expanded-height | 扩展时卡片高度, 盖入取景端 | min(900px, 85dvh) |
--fern-ask-ai-radius | 卡片+发射器角半径 | 12px |
--fern-ask-ai-font-family | 所有部件文本 | ui-sans-serif 堆栈 |
--fern-ask-ai-background | 纸牌、发射器和抽屉表面 | #fff |
--fern-ask-ai-background-dark | 暗模式下相同的表面 | Radix 黑暗步骤 1 |
--fern-ask-ai-text | 主文本颜色, 与表面对齐 | --grayscale-a12 |
--fern-ask-ai-icon | 闪光的图标 | 下面的重音 |
--fern-ask-ai-accent | 闪光, 发送按钮, 答案链接 | 灰色,或 accentColor 设定时 |
中间颜色
部件默认是中性的——灰色花花,灰色发出按钮,灰色链接——因为它 在你的页面,而不是在 Fern Docs 网站。 用一个道具给它你的品牌颜色:
<AskAiChat domain="https://docs.example.com" accentColor="#6b4eff" />
一个颜色覆盖两个主题:一个光和一个暗变体从它衍生出来,一个道克的方式
网站从 accent-primary 内 docs.yml您的呼声被保留, 它的光度是
在一个窗内,它反面读取它背后的地表
消失在黑暗的卡片上, 苍白的不洗出光线.
在CSS中,对于不愿通过道具的主机来说,相当于
--fern-ask-ai-accent。它是两个主题的单一平面颜色,因此不同于 accentColor 这个
不会有每个主题的调整; 闪光点保持自己的钩子,所以它们可以不同:
:root {
--fern-ask-ai-accent: #6b4eff;
--fern-ask-ai-icon: #1ba32a;
}
对于更精细的控制,每个步骤的口音尺度 部件从这些有它自己的
--widget-accent-* 钩子( r)--widget-accent-9, (中文(简体) ), --widget-accent-a11(中文(简体) ).
为了匹配您的文档站点的打字功能, 设置您配置的字体
docs.yml —— 说 --font-body 也获得荣誉, 因此从 docs 主题工作复制的值是
(单位:千美元)--fern-ask-ai-font-family 如果两者都设定,则优先:
:root {
--fern-ask-ai-font-family: "GT-Planar", sans-serif;
}
部件不能自动取出 : Docs 站点从中获取字体 docs.yml, (中文(简体) ),
而部件的 CSS 被故意从主机页面隔离,所以它既不继承
您的字体, 也看不到文档站点的字体 .
:root {
--fern-ask-ai-width: 460px;
--fern-ask-ai-offset: 32px;
--fern-ask-ai-radius: 16px;
}
默认位居普通主机UI上方(如:MUI应用条为1100,模式为1300). 若为
您的网站在右下方已有聊天部件, 移动我们的 placement。 。 。 .
黑暗模式
默认情况下, 部件会遵循您网站的主题 。 如果你标出黑暗模式 尾风和
next-themes 这样做 -- -- a dark 类(或 data-theme="dark"继续 <html> 或者说 <body> - 没错
当访客使用您的主题切换时, 自动拾起并实时切换 。 站点
标出没有使光.
部件的样式无论怎样都保持隔离: .dark 类是 读取从来没有继承过,
所以,你的CSS没有漏入.
要锁定一个模式, 或遵循操作系统而不是您的网站 :
<AskAiChat domain="https://docs.example.com" theme="dark" />
<AskAiChat domain="https://docs.example.com" theme="auto" />
"auto" 音轨 prefers-color-scheme。这不是默认,因为设置描述
访问者,而不是你的网站:一个永久的光线网站不应该有黑暗的部件
任何人在黑暗模式下浏览.
两种模式都来自同一种口音和灰度尺度,因此表面通常是
唯一值得称道的事. --fern-ask-ai-background-dark 倒计时
--fern-ask-ai-background因此,一个品牌表面仍然适用于两种模式:
:root {
--fern-ask-ai-background: #fff;
--fern-ask-ai-background-dark: #0d0d0f;
}
内容安全政策
部件向文件来源Algolia和——当读者给出答案时——Fern's
AI服务,所以 connect-src 需求:
connect-src <your-docs-origin> https://*.algolia.net https://*.algolianet.com https://fai.buildwithfern.com;
选择 fai.buildwithfern.com 块反馈票; 失败记录到
控制台而不是向读者展示.
完整示例
import { SearchModal } from '@fern-api/search-widget';
import '@fern-api/search-widget/styles';
function App() {
return (
<div className="app">
<header>
<h1>My Documentation</h1>
<SearchModal
domain="https://docs.mysite.com"
lang="en"
className="search-trigger"
>
🔍 Search Docs
</SearchModal>
</header>
</div>
);
}
export default App;
特征
- AI 授权搜索: 使用AI生成的回复进行自然语言搜索
- 实时结果: 输入时快速搜索结果
- 代码突出显示: 搜索结果中的语法高亮代码块
- 键盘导航: 导航的全部键盘支持
- 响应设计: 桌面和移动设备工程
- 自定义样式: 样式触发按钮以匹配您的设计
浏览器支持
- Chrome/Edge(最新2个版本)
- Firefox(最新2个版本)
- Safari(最新2个版本)
accentColor 需要相对颜色语法
accentColor 以 CSS 生成光和暗的变体
相对颜色语法
(单位:千美元)oklch(from …)这需要Chrome/Edge 119+、Safari 16.4+或 Firefox 128+ (英语)。 。 。 .
旧浏览器放弃声明,回到部件的中性灰色,所以 部件仍然可以完全辨认——它只是没有品牌。 退化是沉默的 问题主要针对 Firefox ESR : 在那里的访客看到灰色,你在那里配置了品牌颜色.
如果您需要所有颜色, 请设定 --fern-ask-ai-accent - 这是一个平地
没有颜色数学的值,因此它适用于任何浏览器。 权衡的是,一个价值是
用于两个主题, 所以选择一些清晰的东西 在你的光 财务报告和审定财务报表 阴暗的表面.
解决问题
createContext only works in Client Components
渲染部件的文件需要 "use client" 指令——见
Next.js 和其他服务器移交框架。 。 。 .
ReferenceError: document is not defined
开始吧 0.4.0 或更早的升级。 错误来自导入本身, 因此没有
更改您的代码时避免了它; 工作范围是推迟导入
dynamic(() => import(...), { ssr: false })这已经不再需要.
不装入样式
确保导入 CSS 文件 :
import '@fern-api/search-widget/styles';
按钮不可见
部件使 没什么 直到它从您那里获取搜索密钥 domain —— 说
没有按钮,没有占位符。 所以错了 domain 外观与不装入的包件完全相同。 这个
一个信号是控制台出错(Failed to fetch API key) (中文(简体) ). 检查 :
- 这个
domainprop是一个有效的文档站点 URL, 包括议定书 (单位:千美元)https://docs.example.com没有docs.example.com(中文(简体) ) ${domain}/api/fern-docs/search/v2/key直接打开时返回 200- 浏览器控制台
Failed to fetch API key或 CORS/DNS 错误
Modal 不打开
确保安装反应19对等依赖性:
npm install react@19 react-dom@19
如果看到模块解析错误,请确认:
- 您正在使用 React 19( 不是 React 18 或更早)
- 两者
react财务报告和审定财务报表react-dom安装在同一版本
何为被打
除React外, 此软件包会捆绑所有依赖性 :
包含在捆绑中 :
- 国家管理
jotai(中文(简体) ) - 数据获取( E)
swr(中文(简体) ) - AI SDK (英语:
@ai-sdk/react, (中文(简体) ),ai(中文(简体) ) - QQ 搜索用户界面组件
- 所有样式( Tailwind CSS +自定义 SCSS)
提供:
- * 反应19和反应DOM 19
套装大小
游客下载的第一幅画是怎样的:
- Java 脚本 : ~ 1.31米B变精度, ~ 387 KiB 击出
- CSS: 请检查DATE=中的日期值 (帮助) CSS: ~ 842 KiB, (英语), ~43 KiB 击出
加上121个按需取回的懒散块,这是你从磁盘上看到的7个米B的大部分 在安装后——特别是语法高亮语法. 这些不是初始阶段的一部分 下载.
SearchModal 财务报告和审定财务报表 AskAiChat 因为他们几乎分享了全部
捆绑(搜索 UI, AI SDK, Algolia, 语法加亮), 所以只导入其中之一
没有有意义的缩小你的捆绑。 CSS 很大,因为每个选择器都会重写到
坐在部件的容器元素下,这是防止部件样式渗入
的主机页; 该锅炉板是重复的,压缩大约20:1.
让它远离你的关键路径
~ 387 KiB gzipped of JavaScript 是一个真正的成本,除非有人打开,否则不需要这些成本 谈。 懒惰地装上它,所以它从来没有挡住 首先油漆或出现 在你的灯塔分数:
'use client';
import dynamic from 'next/dynamic';
import '@fern-api/search-widget/styles';
// Fetched after the page is interactive, not as part of the initial payload.
const AskAi = dynamic(() => import('@fern-api/search-widget').then((m) => m.AskAiChat));
这是一项业绩选择,而不是一项要求——一个普通的 import 工作,以及 ssr: false 实值
两者均不需要(见
Next.js 和其他服务器移交框架) (中文(简体) ).
样式表是一个单独的条目,所以急于导入,因为上面保持发射器的样式
而不拉入JavaScript.
许可证
Apache-2.0 - 见软件包根中的LICENSE文件.
支助
关于问题,请访问 Fern 平台存储器。 。 。 .