Open Scouts:打造你的专属AI网络侦察兵,24/7监控你关注的一切

摘要

Open Scouts是一款AI驱动的监控平台,能创建自动任务(scouts)持续搜索网络,通过Next.js、Supabase等技术实现,支持邮件通知与语义搜索,本文详解其功能、技术栈、使用步骤及设计与分析体系。

什么是Open Scouts?

想象一下,如果你有一群不知疲倦的侦察兵,每天24小时帮你盯着网络上的各种信息——新开业的餐厅、行业最新动态、甚至是特定产品的价格变动,一旦发现你关心的内容,就立刻向你汇报。这就是Open Scouts能为你做的。

Open Scouts是一个基于AI的监控平台,你可以在上面创建“scouts”——这些其实是自动化的任务,它们会按照你设定的时间间隔运行,持续搜索网络上的信息。不管你是想关注附近新开的咖啡馆、跟踪人工智能领域的最新资讯,还是监控任何其他类型的更新,这些“侦察兵”都会全天候工作,找到你需要的内容后就及时通知你。

open-scouts_4

Open Scouts的技术栈:背后的“硬实力”

要支撑这样一个能持续监控、智能分析并及时通知的平台,背后需要一整套强大的技术体系。Open Scouts用到的技术栈包括:

  • Next.js 16:采用App Router和Turbopack,负责构建前端应用和处理服务器端逻辑,让整个平台运行更高效。
  • React 19:用于构建用户界面,让交互更流畅,用户体验更好。
  • TypeScript:提供类型检查,减少代码错误,让开发更稳健。
  • Tailwind CSS v4:用于样式设计,能快速构建出美观且一致的界面。
  • Supabase:集数据库、认证和边缘函数于一体,简化后端开发。
  • pgvector:处理向量嵌入,支持语义搜索,让搜索更智能,能理解内容的含义。
  • Firecrawl SDK(@mendable/firecrawl-js):负责网页抓取和搜索,帮“侦察兵”获取网络信息。
  • OpenAI API:提供AI代理和嵌入功能,让“侦察兵”具备智能分析能力。
  • Resend:处理邮件通知,确保你能及时收到“侦察兵”的汇报。

如何开始使用Open Scouts?

如果你想亲自试试Open Scouts,按照下面的步骤操作就能快速上手:

准备工作:你需要这些东西

在开始之前,确保你已经准备好了这些:

  • Node.js 18或更高版本
  • 包管理工具(bun、npm或pnpm,默认推荐bun)
  • Supabase账号(可以去supabase.com注册)
  • OpenAI API密钥(从platform.openai.com获取)
  • Firecrawl API密钥(来自firecrawl.dev)
  • Resend API密钥(从resend.com获取,用于邮件通知)
  • Google Cloud Console账号(可选,用于Google OAuth登录)

步骤1:克隆代码并安装依赖

首先,把代码克隆到你的本地,然后安装需要的依赖。打开终端,运行这些命令:

git clone https://github.com/leonardogrig/open-scout
cd open-scout
bun install  # 如果你用npm或pnpm,就换成npm install或pnpm install

步骤2:创建Supabase项目

Supabase是整个平台的后端核心,所以需要先创建一个Supabase项目:

  1. 打开supabase.com的控制台(supabase.com/dashboard)
  2. 创建一个新项目
  3. 等待项目完成配置(这可能需要一点时间)

步骤3:启用必要的扩展

在Supabase项目里,有些扩展是必须启用的,它们能让平台的各种功能正常工作:

  1. 进入Supabase控制台的“Database → Extensions”
  2. 搜索并启用这些扩展:

    • pg_cron:用于定时任务,让“侦察兵”能按计划运行
    • pg_net:支持从数据库发送HTTP请求
    • vector:用于AI驱动的语义搜索,分析执行摘要
    • supabase_vault:安全存储凭证,通常默认已启用

步骤4:设置环境变量

环境变量里存储了各种API密钥和配置信息,需要正确设置:

  1. 在项目根目录创建一个.env文件,可以通过复制示例文件来做:

    cp .env.example .env
    
  2. 然后在.env文件里填写你的实际信息。.env.example文件里有详细说明,还附带了获取每个API密钥的链接,照着做就行。

步骤5:运行数据库设置

这一步会配置数据库表、权限等关键内容,让平台能正常工作:

  1. 首先关联你的Supabase项目(这是同步密钥必须的):

    bunx supabase login        # 登录Supabase CLI,只需要做一次
    bunx supabase link --project-ref <your-project-ref>  # 项目ID在Supabase控制台的URL里能找到
    
  2. 然后运行设置脚本:

    bun run setup:db  # 用npm或pnpm的话,就换成npm run setup:db或pnpm run setup:db
    

这个脚本会做很多重要的事情:

  • 创建所有需要的表(比如scoutsscout_executionsscout_execution_steps等)
  • 添加用户认证支持(用户ID列、行级安全策略)
  • 启用实时订阅功能
  • 为AI生成的执行摘要设置向量嵌入
  • 配置可扩展的调度器架构(pg_cron + pg_net + vault)
  • 自动把Supabase URL和服务角色密钥存储到vault里
  • 设置用于“侦察兵”调度和清理的定时任务
  • .env文件同步边缘函数的密钥(OPENAI_API_KEY、FIRECRAWL_API_KEY、RESEND_API_KEY)

注意:这个脚本会检查是否启用了必要的扩展(vectorpg_cronpg_net)。如果没启用,会显示提示,你按照提示在Supabase控制台启用后,再重新运行脚本就行。

步骤6:设置身份认证

Open Scouts用Supabase Auth来处理用户认证,支持邮箱/密码和Google OAuth两种方式。

启用邮箱/密码认证(默认已启用)

  1. 进入Supabase控制台的“Authentication → Providers → Email”
  2. 确保“Enable Email Provider”是开启的
  3. 可以根据需要在“Authentication → Email Templates”里配置邮件模板

启用Google OAuth(可选但推荐)

如果你想让用户能用Google账号登录,按下面的步骤做:

  1. 创建Google OAuth凭证

    • 打开Google Cloud Console(console.cloud.google.com)
    • 创建一个新项目或者选择已有的项目
    • 进入“APIs & Services → Credentials”
    • 点击“Create Credentials → OAuth client ID”
    • 应用类型选择“Web application”
    • 添加授权的JavaScript来源:

      • 开发环境:http://localhost:3000
      • 生产环境:https://your-domain.com(换成你的域名)
    • 添加授权的重定向URI:

      • https://<your-project-ref>.supabase.co/auth/v1/callback
    • 复制“Client ID”和“Client Secret”
  2. 在Supabase里配置

    • 进入Supabase控制台的“Authentication → Providers → Google”
    • 开启“Enable Google Provider”
    • 粘贴刚才复制的Client ID和Client Secret
    • 保存设置

步骤7:部署边缘函数

边缘函数负责处理“侦察兵”的执行和邮件发送,需要部署到Supabase Cloud:

bunx supabase functions deploy scout-cron
bunx supabase functions deploy send-test-email

注意:运行setup:db时,密钥(OPENAI_API_KEY、FIRECRAWL_API_KEY、RESEND_API_KEY)会自动同步。如果需要手动更新,可以运行:

bunx supabase secrets set OPENAI_API_KEY=sk-proj-...  # 换成你的实际密钥

步骤8:设置Resend(邮件通知)

当“侦察兵”找到结果时,会通过邮件通知你,这需要配置Resend:

  1. 在resend.com创建一个账号
  2. 从Resend控制台获取API密钥
  3. 把密钥添加到.env文件,然后重新运行setup:db同步,或者手动设置:

    bunx supabase secrets set RESEND_API_KEY=re_...  # 换成你的实际密钥
    
  4. 在resend.com/domains验证一个自定义域名,这样就能发送邮件到任何邮箱了

重要 – 免费套餐限制

  • 没有验证域名的话,Resend只能发送邮件到你的Resend账号邮箱
  • 免费套餐每月包含3000封邮件(每天限100封)

测试邮件设置

  1. 进入应用的“Settings”
  2. 点击“Send Test Email”验证配置是否正确
  3. 查看你的收件箱,看看有没有收到测试邮件

步骤9:配置Firecrawl

Open Scouts用Firecrawl来进行网页抓取和搜索,有两种配置方式:

选项A:标准API密钥(推荐给贡献者)

这种方式最简单,所有用户共享一个API密钥:

  1. 在firecrawl.dev注册账号
  2. 从dashboard(www.firecrawl.dev/app/api-keys)获取API密钥
  3. 添加到.env文件:

    FIRECRAWL_API_KEY=fc-your-key-here  # 换成你的密钥
    
  4. 设置边缘函数的密钥:

    npx supabase secrets set FIRECRAWL_API_KEY=fc-your-key-here
    

选项B:合作伙伴集成(用于生产环境部署)

如果你要部署Open Scouts供多个用户使用,想管理每个用户的API密钥,就用这种方式:

  1. 联系Firecrawl获取合作伙伴密钥
  2. .env文件里设置合作伙伴密钥:

    FIRECRAWL_API_KEY=your-partner-key  # 换成你的合作伙伴密钥
    
  3. 设置边缘函数的密钥:

    npx supabase secrets set FIRECRAWL_API_KEY=your-partner-key
    

合作伙伴集成的工作原理

  • 用户注册时,会自动为他们创建一个唯一的Firecrawl API密钥
  • 每个用户的使用情况会单独跟踪
  • 密钥安全地存储在user_preferences表中
  • 如果用户的密钥失效,系统会自动切换到共享的合作伙伴密钥
  • 用户可以在“Settings → Firecrawl Integration”查看他们的连接状态

优势

  • 能更好地跟踪每个用户的使用情况
  • 可以单独撤销某个用户的密钥
  • 自动为新用户分配密钥
  • 自我修复:检测到无效密钥时自动切换到备用方案

注意:合作伙伴集成完全向后兼容。如果没有合作伙伴密钥,系统会像选项A那样用共享密钥工作。

步骤10:运行开发服务器

一切设置好后,就可以启动开发服务器,看看Open Scouts的样子了:

bun run dev  # 用npm或pnpm的话,就换成npm run dev或pnpm run dev

然后打开http://localhost:3000,就能访问应用了。

Open Scouts是如何工作的?

了解了如何搭建,再来看看它具体是怎么运作的,从用户登录到“侦察兵”工作,每个环节都有其逻辑。

用户认证流程

  1. 公共首页:用户不用登录就能浏览 landing 页
  2. 创建侦察兵:当用户输入查询并按回车时,会提示他们登录
  3. 登录/注册:用户可以通过邮箱/密码或Google OAuth认证
  4. 继续流程:认证后,会自动继续创建“侦察兵”的流程
  5. 用户隔离:每个用户只能看到和管理自己的“侦察兵”

“侦察兵”系统

  1. 创建侦察兵:定义你想监控的内容(比如“侦察最近我附近的印度餐厅”或“侦察AI新闻”)
  2. AI代理设置:系统会自动配置搜索查询和策略
  3. 设置频率:选择运行频率(每小时、每3天、每周等)
  4. 配置通知:在设置里添加你的邮箱,当“侦察兵”找到结果时就会收到提醒
  5. 持续监控:调度器每分钟检查一次,触发到期的“侦察兵”
  6. AI摘要:每次成功执行都会生成一个简洁的一句话摘要,并带有语义嵌入
  7. 收到通知:如果配置了邮箱,当“侦察兵”找到新结果时会收到邮件提醒
  8. 查看结果:在“侦察兵”页面实时查看所有发现,包括AI生成的摘要

手动执行

在任何“侦察兵”页面点击“Run Now”按钮,就能立即触发执行,不用等定时任务。

邮件通知

当“侦察兵”找到结果时,会自动向你的账号邮箱发送邮件提醒:

  • 自动发送:只有当“侦察兵”成功找到结果时才会发邮件
  • 内容丰富:美观的HTML邮件,包含“侦察兵”的结果和链接
  • 测试:可以用设置里的“Send Test Email”按钮验证设置是否正确

邮件服务:由Resend提供(免费套餐每月3000封邮件)

注意:在Resend的免费套餐中,如果没有验证域名,邮件只能发送到你的Resend账号邮箱。要发送到任何邮箱,需要在resend.com/domains验证一个自定义域名。

架构设计

Open Scouts的架构各个部分分工明确,共同支撑整个平台的运行:

  • 前端:Next.js应用,通过Supabase Realtime实现实时更新
  • 数据库:PostgreSQL(Supabase),用pg_cron做调度,pgvector支持语义搜索
  • 认证:Supabase Auth(邮箱/密码 + Google OAuth)
  • AI代理:OpenAI GPT-4,带函数调用(搜索和抓取工具)
  • AI摘要:自动生成一句话摘要,每个成功执行都带有向量嵌入
  • 边缘函数:基于Deno的无服务器函数,协调代理执行
  • 网页抓取:Firecrawl API,用于搜索和内容提取(通过合作伙伴集成支持每个用户的API密钥)

可扩展的调度器架构

Open Scouts采用的调度器模式能支持成千上万的“侦察兵”:

每分钟:
pg_cron → dispatch_due_scouts() → 找到到期的侦察兵 → pg_net HTTP POST
                                                          ↓
                                    ┌──────────────────────┼──────────────────────┐
                                    ↓                      ↓                      ↓
                              边缘函数          边缘函数          边缘函数
                              (侦察兵A)              (侦察兵B)              (侦察兵C)
                              [独立运行]             [独立运行]             [独立运行]
  • 调度器(SQL):通过pg_cron每分钟运行一次,查询到期的“侦察兵”,并发送单独的HTTP请求
  • 独立执行:每个“侦察兵”在自己的边缘函数实例中运行,有独立的资源(256MB内存,400秒超时)
  • 自动清理:另一个定时任务每5分钟清理一次卡住的执行
  • Vault集成:Supabase凭证安全地存储在vault中,供调度器读取

安全保障

使用任何平台,安全都是重中之重,Open Scouts在安全方面做了这些工作:

  • 行级安全(RLS):所有数据库表都有RLS策略,确保用户只能访问自己的数据
  • 用户隔离:“侦察兵”、消息和执行都与认证用户绑定
  • 安全认证流程:OAuth令牌和会话由Supabase Auth管理
  • 服务角色:服务器端操作(定时任务、边缘函数)使用服务角色获取特权访问
  • API密钥存储:Firecrawl API密钥(使用合作伙伴集成时)存储在服务器端的user_preferences中,从不会暴露给客户端

Firecrawl设计系统:让界面既美观又易用

Open Scouts的界面是基于Firecrawl设计系统构建的,这个系统让整个应用的视觉风格保持一致,同时也方便开发人员快速构建界面。

设计系统概述

Firecrawl设计系统围绕模块化组件架构构建,位于components-new/目录下。它整合了多个UI库,为应用提供一致的视觉语言。

核心技术

  • Tailwind CSS:带自定义配置的实用优先CSS框架
  • shadcn/ui:基于Radix UI的高质量React组件
  • 自定义组件:应用特定的共享组件

目录结构

components-new/
├── ui/                    # 核心UI组件
│   ├── shadcn/           # shadcn/ui组件
│   ├── magic/            # Magic UI动画组件
│   ├── tremor/           # Tremor数据可视化组件
│   └── motion/           # 运动和动画工具
├── shared/               # 共享应用组件
│   ├── icons/            # 图标组件和品牌资产
│   ├── buttons/          # 自定义按钮组件
│   ├── cards/            # 卡片组件
│   ├── effects/          # 视觉效果和动画
│   ├── layout/           # 布局工具
│   └── ui/               # 共享UI工具
├── app/                  # 应用特定组件
│   ├── brand/            # 品牌相关组件
│   ├── pricing/          # 定价页面组件
│   └── (home)/           # 首页组件
└── providers/            # 上下文提供者和主题管理

颜色系统

设计系统使用在colors.jsonstyles/colors.json中定义的全面调色板,支持明暗模式下的一致主题。

颜色分类

热力色(Heat Colors)

主要品牌色,有不同的透明度级别:

  • heat-4heat-100:橙红色品牌色(#fa5d19),透明度从4%到100%
强调色(Accent Colors)

用于不同UI状态和上下文的语义颜色:

  • accent-black:深中性色(#262626)
  • accent-white:纯白色(#ffffff)
  • accent-amethyst:紫色强调(#9061ff)
  • accent-bluetron:蓝色强调(#2a6dfb)
  • accent-crimson:红色强调(#eb3424)
  • accent-forest:绿色强调(#42c366)
  • accent-honey:黄色强调(#ecb730)
透明度变体(Alpha Variants)

用于分层和深度的透明叠加:

  • black-alpha-1black-alpha-88:黑色,透明度1%到88%
  • white-alpha-56white-alpha-72:白色,透明度56%和72%
UI颜色

界面元素的特定颜色:

  • border-faintborder-mutedborder-loud:边框颜色变体
  • illustrations-faintillustrations-mutedillustrations-default:插图颜色
  • background-lighterbackground-base:背景颜色变体

颜色使用

颜色通过CSS自定义属性集成到Tailwind CSS中:

const colors = Object.keys(colorsJson).reduce(
  (acc, key) => {
    acc[key] = `var(--${key})`;
    return acc;
  },
  {} as Record<string, string>
);

这使得动态主题和一致的颜色使用成为可能:

<div className="bg-heat-100 text-accent-white">
  主要品牌样式
</div>

<div className="border border-border-muted bg-background-base">
  微妙的界面元素
</div>

Tailwind配置

Tailwind配置(tailwind.config.ts)扩展了默认主题,包含专门为Firecrawl设计系统设计的自定义排版、间距和工具类。

排版比例

设计系统包含全面的排版比例,带有语义命名:

标题
  • title-h1:60px,行高64px,字间距-0.3px
  • title-h2:52px,行高56px,字间距-0.52px
  • title-h3:40px,行高44px,字间距-0.4px
  • title-h4:32px,行高36px,字间距-0.32px
  • title-h5:24px,行高32px,字间距-0.24px
正文
  • body-x-large:20px,行高28px,字间距-0.1px
  • body-large:16px,行高24px
  • body-medium:14px,行高20px,字间距0.14px
  • body-small:13px,行高20px
  • body-input:15px,行高24px
标签
  • label-x-large:20px,行高28px,字重450
  • label-large:16px,行高24px,字重450
  • label-medium:14px,行高20px,字重450
  • label-small:13px,行高20px,字重450
  • label-x-small:12px,行高20px,字重450
等宽字体
  • mono-medium:14px,行高22px
  • mono-small:13px,行高20px,字重500
  • mono-x-small:12px,行高16px

字体家族

  • 无衬线(Sans):SuisseIntl(主要),系统 fallback
  • 等宽(Mono):Geist Mono,系统 fallback
  • ASCII:Roboto Mono,系统 fallback

自定义工具类

配置包含几个自定义工具类:

边框工具
  • .inside-border:绝对定位的边框叠加
  • .inside-border-x:水平边框叠加
  • .inside-border-y:垂直边框叠加
  • .mask-border:用于边框效果的CSS遮罩
定位工具
  • .center-x:水平居中
  • .center-y:垂直居中
  • .center:完全居中
  • .flex-center:Flexbox居中
布局工具
  • .overlay:全屏叠加定位
  • .text-gradient:文本渐变效果
自定义尺寸工具
  • cw-{size}:居中宽度定位
  • ch-{size}:居中高度定位
  • cs-{size}:居中正方形尺寸
  • cmw-{maxWidth},{padding}:带内边距的居中最大宽度
  • mw-{maxWidth},{padding}:带内边距的最大宽度

重要:自定义尺寸系统

Firecrawl设计系统使用自定义尺寸系统,其中数值等于实际像素,而不是像标准Tailwind那样的rem单位。

这意味着什么

tailwind.config.ts中,定义了一个自定义的sizes对象(第16-37行),它将数字映射到像素值:

const sizes = Array.from({ length: 1000 }, (_, i) => i).reduce(
  (acc, curr) => {
    acc[curr] = `${curr}px`;  // 3 = "3px",8 = "8px",100 = "100px"
    return acc;
  },
  { /* 分数百分比 */ }
);

这个sizes对象然后应用于多个CSS属性(第337-344行):

  • spacing – 影响内边距(p-*)、外边距(m-*)、间距(gap-*
  • width – 影响宽度(w-*
  • height – 影响高度(h-*
  • size – 影响size-*工具(宽度+高度简写)
  • inset – 影响定位(top-*left-*等)
与标准Tailwind的比较
类名 标准Tailwind Firecrawl系统
w-3 0.75rem(12px) 3px
h-8 2rem(32px) 8px
size-4 1rem(16px) 4px
p-12 3rem(48px) 12px
gap-24 6rem(96px) 24px
应该怎么用

对于间距(内边距、外边距、间距):

<div className="p-24 gap-16 mb-8">  {/* 24px内边距,16px间距,8px底边距 */}

对于边框半径(基于像素):

<div className="rounded-8">  {/* 8px边框半径 */}
<div className="rounded-6">  {/* 6px边框半径 */}
<div className="rounded-4">  {/* 4px边框半径 */}

对于边框宽度(明确的像素):

<div className="border-1">  {/* 1px边框 */}

避免用于组件高度/宽度

{/* 错误 - 按钮会只有9px高! */}
<Button className="h-9" />

{/* 错误 - 图标会只有4px × 4px! */}
<Icon className="size-4" />
与组件一起使用

问题:许多UI组件(按钮、图标、输入框等)使用h-*size-*工具类,它们期望的是rem-based值,但得到的是像素值。

解决方案:对于应该更大的高度/宽度,使用明确的像素值:

{/* 不要用size-4(4px),用明确的值 */}
<Icon className="w-16 h-16" />  {/* 16px × 16px图标 */}

{/* 或者对非间距尺寸使用style属性 */}
<Icon style={{ width: '1rem', height: '1rem' }} />  {/* 16px × 16px */}
常见组件修复

输入组件

// 错误 - 创建9px高的输入框
<Input className="h-9 px-3 py-1 rounded-md border text-sm" />

// 正确 - 合适的40px高输入框
<Input className="h-40 px-12 py-8 rounded-6 border-1 text-body-input" />

按钮组件

// 错误 - 创建9px高的按钮
<Button className="h-9 px-4 gap-2">点击我</Button>

// 正确 - 合适的36px高按钮
<Button className="h-36 px-16 gap-8">点击我</Button>

文本区域组件

// 错误 - 创建10px的内边距
<Textarea className="py-10 px-12" />

// 正确 - 合适的内边距
<Textarea className="py-16 px-16" />

图标组件

// 错误 - 创建4px × 4px的图标
<Icon className="size-4" />

// 正确 - 合适的16px × 16px图标
<Icon className="w-16 h-16" />
迁移指南

从标准Tailwind或其他项目移植组件时:

  1. 间距保持不变p-24 = 24px内边距 ✓
  2. 高度需要转换h-9(36px)→ h-144(144px)或使用style
  3. 尺寸需要转换size-4(16px)→ size-64(64px)或使用style
  4. 边框半径 – 使用像素数:rounded-8而不是rounded-lg
  5. 边框宽度 – 明确指定:border-1而不是border
排版例外

排版使用语义尺寸(不受此系统影响):

<h1 className="text-title-h3">  {/* 使用排版配置中的40px */}
<p className="text-body-medium">  {/* 使用排版配置中的14px */}

边框半径系统

边框半径使用基于像素的数值(不是标准Tailwind名称):

// 可用:rounded-{0-32},1px递增
rounded-0   // 0px
rounded-4   // 4px(小按钮、输入框)
rounded-6   // 6px(卡片、模态框)
rounded-8   // 8px(大卡片、容器)
rounded-16  // 16px(非常圆)
rounded-32  // 32px(最大圆角)
rounded-full // 999px(正圆)

常见用法

  • 小UI元素(按钮、徽章):rounded-4
  • 中型组件(输入框、小卡片):rounded-6
  • 大型组件(卡片、模态框):rounded-8
  • 圆形/药丸形:rounded-full

不要使用标准Tailwind名称

{/* 错误 - 这些在配置中不存在 */}
<div className="rounded-sm rounded-md rounded-lg rounded-xl" />

{/* 正确 - 使用像素数 */}
<div className="rounded-4 rounded-6 rounded-8 rounded-16" />

透明度系统

自定义透明度范围0-99(基于百分比):

// 可用:opacity-{0-99}
opacity-0   // 0%(不可见)
opacity-10  // 10%
opacity-50  // 50%(半透明)
opacity-80  // 80%
opacity-100 // 100%(完全不透明)- 尽量少用,优先用opacity-99

示例

<div className="opacity-50 hover:opacity-100">悬停时淡入</div>
<div className="bg-black opacity-20">微妙的叠加层</div>

过渡系统

自定义过渡时间和持续时间:

时间函数(默认)

transition-timing-function: cubic-bezier(0.25, 0.1, 0.25, 1)

所有transition类默认使用这种缓动。

过渡持续时间

可用:duration-{0-59},每个数字 = n × 50ms

duration-0   // 0ms(即时)
duration-4   // 200ms(默认,快速)
duration-10  // 500ms(中等)
duration-20  // 1000ms(慢)
duration-40  // 2000ms(很慢)

示例

<div className="transition-all duration-4">快速过渡(200ms)</div>
<div className="transition-opacity duration-10">中等淡出(500ms)</div>

动画

可用的自定义关键帧动画:

// 手风琴动画
animate-accordion-down  // 平滑的手风琴展开
animate-accordion-up    // 平滑的手风琴收起

// 淡入动画
animate-fade-in        // 从0到100%不透明度的淡入
animate-fade-up        // 淡入 + 上移10px

// 特殊效果
animate-screenshot-scroll  // 15秒无限滚动动画
animate-selection-pulse-green  // 绿色选择脉冲
animate-button-press   // 按钮按下效果(缩小/放大)

用法

<div className="animate-fade-in">挂载时淡入</div>
<button className="animate-button-press">按我</button>

常见间距值

为保持一致性,推荐使用这些间距值:

微间距(紧凑布局)

  • gap-4p-4m-4 = 4px
  • gap-8p-8m-8 = 8px

标准间距(最常用)

  • gap-12p-12m-12 = 12px
  • gap-16p-16m-16 = 16px
  • gap-24p-24m-24 = 24px

大间距(区块、容器)

  • gap-32p-32m-32 = 32px
  • gap-48p-48m-48 = 48px
  • gap-64p-64m-64 = 64px

超大间距(页面布局)

  • gap-80p-80m-80 = 80px
  • gap-96p-96m-96 = 96px
  • gap-128p-128m-128 = 128px

分数百分比(也可用):

w-1/2   // 50%
w-1/3   // 33.3%
w-2/3   // 66.6%
w-1/4   // 25%
w-1/6   // 16.6%
w-5/6   // 83.3%

响应式断点

screens: {
  xs: { min: "390px" },
  "xs-max": { max: "389px" },
  sm: { min: "576px" },
  "sm-max": { max: "575px" },
  md: { min: "768px" },
  "md-max": { max: "767px" },
  lg: { min: "996px" },
  "lg-max": { max: "995px" },
  xl: { min: "1200px" },
  "xl-max": { max: "1199px" }
}

组件架构

UI组件(components-new/ui/

UI层由三个主要组件库组成:

shadcn/ui组件

高质量、可访问的React组件:

  • 表单控件:ButtonInputSelectCheckboxSwitch
  • 布局:CardSheetDialogTabsAccordion
  • 导航:NavigationMenuDropdownMenuContextMenu
  • 反馈:ToastAlertProgressBadge
  • 数据:TableDataTableCalendar
Magic UI组件

带动画和交互的组件:

  • animated-shiny-text:闪光文本效果
  • animated-list:列表动画
  • dot-pattern:背景图案
  • dock:macOS风格的 dock 组件
  • ripple:波纹效果
  • gradual-spacing:文本间距动画
Tremor组件

数据可视化和仪表板组件:

  • 图表:LineChartBarChartAreaChart
  • 控件:ButtonBadgeDropdown
  • 布局:CardCalendarDatePicker
  • 进度:ProgressBar

共享组件(components-new/shared/

图标(shared/icons/

品牌和工具图标,有组织的导出:

// 品牌图标
export { default as SymbolWhite } from './symbol-white';
export { default as SymbolColored } from './symbol-colored';
export { default as WordmarkWhite } from './wordmark-white';
export { default as WordmarkColored } from './wordmark-colored';

// 工具图标
export { default as AnimatedLogo } from './animated-logo';
export { default as Check } from './check';
export { default as GitHub } from './github';
按钮(shared/buttons/

带品牌样式的自定义按钮组件:

export { SlateButton } from './slate-button';
export { HeatButton } from './heat-button';
export { FireActionLink } from './fire-action-link';
布局组件
  • curvy-rect:曲线矩形形状
  • animated-height:高度动画
  • animated-width:宽度动画
  • unified-blur-overlay:背景模糊效果
效果和动画
  • flame/:火焰动画效果
  • animated-beam:连接光束动画
  • data-sources-beam:数据流可视化

应用组件(components-new/app/

品牌组件(app/brand/

特定于品牌展示的组件:

  • brand-hero.tsx:品牌页面hero区域
  • brand-assets-copy.tsx:资产复制功能
  • brand-assets-download.tsx:资产下载功能
  • brand-group.tsx:品牌资产分组
  • firecrawl-logo.tsx:Logo组件变体
  • firecrawl-wordmark.tsx:文字标志组件

品牌hero组件示例:

export default function BrandHero() {
  return (
    <section className='max-w-[1112px] mx-auto -mt-1'>
      <SectionHead
        action={(
          <a className="contents" href="/brand/brand-assets.zip" download>
            <Button className='mx-auto' size='large' variant='primary'>
              下载品牌资产
            </Button>
          </a>
        )}
        description="欢迎来到Firecrawl品牌中心..."
        title={<><span className="text-heat-100">Firecrawl </span>品牌资产</>}
        titleClassName='text-title-h3'
      >
        <DeveloperFlame />
      </SectionHead>
    </section>
  );
}

品牌资产

资产组织(public/brand/

品牌资产组织在public/brand/目录中,包含全面的logo和营销材料:

Logo变体
  • firecrawl-logo.svg/png:主要logo
  • firecrawl-light-logo.svg/png:浅色主题变体
  • firecrawl-wordmark.svg/png:纯文字标志
  • firecrawl-light-wordmark.svg/png:浅色文字标志变体
  • firecrawl-icon.png:应用图标
  • firecrawl-app-icon.png:应用图标变体
特殊资产
  • firecrawl-logo-transparent.png:透明背景logo
  • firecrawl-logo-with-fire.png:带火焰元素的logo
  • logo_fire.png:独立火焰元素
营销材料
  • scrape-data-from-any-site--firecrawl.jpg
  • turn-websites-into-llm-ready-data--firecrawl.jpg
  • we-handle-all-the-hard-stuff--firecrawl.jpg
  • trusted-by-devs-at-top-companies--firecrawl.jpg
资产分发
  • brand-assets.zip:完整的品牌资产包,可供下载

品牌使用指南

Logo使用

  • 使用firecrawl-logo.svg作为主要品牌展示
  • 在深色背景上使用firecrawl-light-logo.svg
  • 空间有限时使用firecrawl-wordmark.svg
  • 保持适当的间距和尺寸比例

颜色使用

  • 主要品牌色:heat-100(#fa5d19)
  • 强调色用于突出显示和CTA时要适度
  • 保持足够的对比度以确保可访问性

PostHog集成:了解用户如何使用Open Scouts

为了知道用户是如何使用Open Scouts的,以及平台的运行状况,Open Scouts集成了PostHog进行产品分析。

我们跟踪什么

用户旅程

我们跟踪从注册到成为活跃用户的完整用户旅程:

  1. 认证 – 注册、登录和OAuth流程
  2. 侦察兵创建 – 用户创建新的监控侦察兵时
  3. 配置 – 侦察兵设置的进度(目标、查询、位置、频率)
  4. 激活 – 侦察兵启用自动监控时
  5. 参与度 – 手动运行、查看结果、管理侦察兵

执行指标

服务器端跟踪侦察兵执行性能:

  • 执行成功/失败率
  • 持续时间和步骤数
  • 重复检测(结果与之前的运行匹配时)
  • 邮件通知送达情况

关键漏斗

激活漏斗

衡量用户从注册到拥有活跃侦察兵的效率:

注册 → 创建侦察兵 → 完成配置 → 激活侦察兵

参与漏斗

衡量用户的持续参与度:

查看结果 → 手动触发 → 查看邮件通知

产品健康指标

指标 描述
执行成功率 成功完成的侦察兵运行百分比
重复检测率 发现已报告信息的运行百分比
配置完成率 已创建的侦察兵中完全配置的百分比
激活率 已配置的侦察兵中用户实际开启的百分比
邮件送达率 成功发送的通知百分比

设置

环境变量

NEXT_PUBLIC_POSTHOG_KEY=your_project_api_key
NEXT_PUBLIC_POSTHOG_HOST=https://us.posthog.com  # 可选

PostHog密钥还必须提供给Supabase边缘函数,用于服务器端跟踪。

架构

┌─────────────────────────────────────────────────────────────┐
│                        浏览器                               │
│  ┌─────────────────────────────────────────────────────┐    │
│  │  posthog-js(instrumentation-client.ts)             │    │
│  │  - 用户识别                                        │    │
│  │  - 页面浏览和UI交互                                 │    │
│  │  - 客户端事件                                       │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                    Next.js服务器                            │
│  ┌─────────────────────────────────────────────────────┐    │
│  │  posthog-node(lib/posthog-server.ts)               │    │
│  │  - 服务器端事件捕获                                 │    │
│  │  - API路由跟踪                                      │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                 Supabase边缘函数                          │
│  ┌─────────────────────────────────────────────────────┐    │
│  │  HTTP捕获API(supabase/functions/.../posthog.ts)│    │
│  │  - 执行生命周期事件                                 │    │
│  │  - 邮件通知跟踪                                     │    │
│  │  - 性能指标                                          │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘

事件参考

认证

事件 触发时机
user_signed_up 新账户创建时
user_logged_in 现有用户登录时
google_auth_initiated 用户点击Google OAuth时

侦察兵管理

事件 触发时机
scout_created 新侦察兵创建时
scout_configuration_completed 所有必填字段填写完成时
scout_activated 侦察兵启用自动监控时
scout_deactivated 侦察兵禁用时
scout_deleted 侦察兵永久删除时

执行(服务器端)

事件 触发时机
scout_execution_started 执行开始时
scout_execution_completed 执行成功完成时
scout_execution_failed 执行出错时
scout_duplicate_detected 结果与之前的运行匹配时
scout_email_notification_sent 尝试发送邮件通知时

用户参与

事件 触发时机
scout_execution_triggered 用户手动运行侦察兵时
scout_results_viewed 用户查看执行结果时
execution_history_cleared 用户清除执行历史时

设置

事件 触发时机
test_email_sent 用户发送测试通知时
firecrawl_key_regenerated 用户重新生成API密钥时
location_updated 用户更改默认位置时

添加新事件

客户端

import posthog from "posthog-js";

posthog.capture("event_name", {
  relevant_property: "value",
});

服务器端(API路由)

import { getPostHogClient } from "@/lib/posthog-server";

const posthog = getPostHogClient();
posthog.capture({
  distinctId: user.id,
  event: "event_name",
  properties: { ... },
});

边缘函数

import { captureEvent } from "./posthog.ts";

captureEvent({
  event: "event_name",
  distinctId: userId,
  properties: { ... },
});

隐私考虑

  • 用户识别使用Supabase user.id(不是邮箱)作为主要标识符
  • 邮箱仅作为用户属性存储,用于支持目的
  • 事件属性中不包含任何个人身份信息(PII)
  • 使用PostHog的代理路径(/ingest)提高数据收集的可靠性

常见问题(FAQ)

关于Open Scouts的基本问题

  1. Open Scouts适合什么场景使用?
    只要你需要持续监控网络上的特定信息,都可以用它。比如跟踪行业新闻、关注新开业的店铺、监控产品价格变化等。

  2. 使用Open Scouts需要编程基础吗?
    按照步骤设置的话,不需要深入的编程知识,但需要能操作终端和配置一些文件。

技术与设置相关

  1. 必须使用所有提到的技术吗?
    是的,这些技术是平台正常运行的基础,比如Supabase提供数据库和认证,Firecrawl负责网页抓取等。

  2. 免费套餐够用吗?
    Resend的免费套餐每月有3000封邮件,适合个人试用。如果是企业使用,可能需要升级套餐。

  3. 如何知道我的“侦察兵”是否在正常工作?
    可以在应用中查看“侦察兵”的执行历史,也可以通过邮件通知来确认。如果有问题,执行历史中会显示错误信息。

设计系统相关

  1. 为什么设计系统要用自定义尺寸,而不是标准Tailwind?
    这是为了更精确地控制界面元素的大小,确保设计的一致性,尤其是在需要像素级精确的场景下。

  2. 可以修改设计系统的颜色吗?
    可以通过修改colors.json和Tailwind配置来调整颜色,但建议保持品牌色的一致性。

PostHog分析相关

  1. 跟踪的用户数据安全吗?
    安全,跟踪时使用的是用户的ID而非个人信息,且不包含PII在事件属性中。

  2. 可以关闭PostHog分析吗?
    目前没有直接的开关,但可以通过不设置PostHog的环境变量来禁用。

总结

Open Scouts是一个功能强大的AI监控平台,它能帮你24小时盯着网络上的信息,让你不会错过任何重要内容。通过合理设置,你可以创建各种“侦察兵”来满足自己的需求。

它的技术栈涵盖了前端、后端、AI、数据库等多个领域,确保了平台的稳定性和扩展性。设计系统则让界面既美观又易用,而PostHog集成则能帮助开发者了解用户使用情况,持续改进产品。

如果你想拥有一个自己的AI网络侦察兵团队,不妨按照文中的步骤试试Open Scouts,相信它会给你带来不少便利。