创建项目与品牌配置

创建 Saavo 项目,完成本地初始化、网站文案和主题颜色配置。

上一篇已经确定了产品范围和技术方案。这一篇创建正式的 Saavo 项目,完成本地运行、网站文案和主题颜色配置。

创建 WebpageToPDF 项目

原型验证完成后,就可以开始使用 Saavo 创建正式项目。

前面的原型阶段主要用来验证功能和方案是否可行。进入正式开发后,需要先准备好开发环境,再把已经验证过的功能逐步迁移到 Saavo 模板中。

这次教程使用 Windows,终端使用 PowerShell,开发工具使用 Cursor 和 Codex,后面的内容也都会基于这套环境来演示。

这只是我个人使用的工具组合,并不是 Saavo 的硬性要求。如果你已经有自己习惯的编辑器或 AI 编程工具,继续使用就可以,不需要为了跟着教程专门更换。

准备账号和基础环境

前面的准备工作已经介绍过具体步骤。如果你是直接从这篇实战教程开始看的,先确认下面几项:

  1. Saavo 账号和模板权限:先完成注册和购买。购买后,可以在账户中心的“我的产品”中看到 Saavo Starter。后面使用 CLI 授权时,也需要登录这个拥有模板权限的账号。
  2. Git:按照安装 Git完成安装,用来保存代码修改记录。后面让 AI 改代码时,也方便检查它具体改了什么。
  3. Node.js 和 npm:按照安装 Node.js完成安装。当前 CLI 和模板要求 Node.js 至少为 22.13.0,按准备工作里的步骤安装 Node.js 24 LTS 即可,npm 会一起安装。
  4. Cloudflare 账号:这个项目最终会部署到 Cloudflare,需要按照注册 Cloudflare完成注册、邮箱验证和 R2 开通。不过,只在本地运行 Saavo 模板时,还不需要远程资源,这一项可以在部署前补齐,也不用现在就手动创建数据库、Bucket 和队列。

安装完成后,重新打开 PowerShell,检查命令是否可用:

git --version
node -v
npm -v

三条命令都应该输出版本号。如果提示找不到命令,先回到对应的安装文档处理。若 PowerShell 提示禁止执行 npm.ps1,参照 Node.js 安装页中的说明,可以先用 npm.cmd、npx.cmd 执行命令,不需要重新安装 Node.js。

域名、邮件、支付和第三方登录不需要一开始就全部配置好,域名我已经注册完毕: webpagetopdf.dev,但本地开发并不依赖域名。现阶段先把模板运行起来,等开发到邮件、付费和部署相关功能时,再配置对应的服务即可。具体可以参考可选服务中的内容。

安装 Cursor 和 Codex

如果你想使用和我一样的工具,还需要安装 Cursor 和 Codex。Cursor 主要用来查看和修改代码、运行终端命令,Codex 则用来分析项目、编写代码和排查问题。Cursor 其实也提供 AI 功能,实际开发时用哪个工具完成任务,按自己的习惯来就可以。

  • Cursor:打开 Cursor 下载页,选择适合自己系统的桌面版并安装。第一次启动时,按提示完成账号登录和基本设置,是否导入其他编辑器的配置,可以自行决定。
  • Codex:这里使用桌面端,按 OpenAI 官方桌面端安装说明下载并安装,Windows 用户可以直接看 Windows 安装页。安装后用自己的 ChatGPT 账号登录,按提示完成初次设置。后面需要让它打开本地项目目录,而不只是把代码复制到聊天窗口里。

这两个工具的账号和 Saavo 账号是分开的,安装完成后,先确认自己要用的 AI 功能可以正常使用,可用模型和额度以各自账号里显示的内容为准。

创建并初始化项目

打开 PowerShell/CMD,进入准备存放项目的上级目录,再执行:

npx saavo-cli@latest create webpagetopdf

这条命令会在当前目录下创建一个 webpagetopdf 文件夹。如果前面的原型项目已经使用了这个名字,换一个目录即可,避免覆盖原来的代码。

第一次运行时,npx 可能会提示安装 saavo-cli,确认安装后继续即可,不需要提前全局安装 CLI。

接下来会打开 Saavo 的浏览器授权页面。登录拥有模板权限的账号,确认授权内容后允许访问,再回到终端。如果账号下有多个可用模板,按提示选择这次使用的模板即可。

模板下载完成后,CLI 会询问是否准备本地开发环境:

Prepare the project for local development?
Installs dependencies, creates .env, and initializes local databases.

这里选择 Yes。CLI 会安装依赖,再调用模板中的 saavo:init,创建本地 .env、生成 SAAS_SECRET 和 Cloudflare 类型、初始化本地 D1 数据库,最后运行 doctor 检查配置。这些步骤不需要自己再做一遍,Wrangler 也会随项目依赖一起安装。

等终端显示 Project is ready for local development,说明本地初始化已经完成。如果跳过了初始化,或者依赖安装、初始化中途失败,先解决终端提示的问题,再进入已经下载好的项目目录补做:

cd webpagetopdf
npm install
npm run saavo:init

完整的授权和初始化截图可以参考创建项目。

打开项目,确认能正常运行

在 Cursor 中打开整个 webpagetopdf 文件夹,不要只打开单个文件。然后在 Codex 中添加本地项目,同样选择这个文件夹。打开后,根目录下应该能看到 package.json、config/、src/ 和 AGENTS.md。

Cursor 打开项目结构

模板已经内置了 AGENTS.md 和 .cursor/rules/ 中的项目规则,使用 AI 修改代码时不要删除这些文件。Cursor 和 Codex 可以同时打开同一个项目,但尽量不要同时修改同一批文件,避免出现内容被覆盖的情况。

在 Cursor 的终端中确认当前目录是 webpagetopdf,然后运行:

npm run dev

用浏览器打开终端中显示的本地地址,先确认首页、文档、登录和注册页面都能正常访问。

本地运行截图

这时看到的应该还是 Saavo 的默认页面,先不要急着迁移原型代码。如果模板本身存在报错,先按照本地运行中的说明完成排查,确认基础环境正常后再继续开发。

此时,初始化脚本已经生成了 .env 文件。里面包含的密钥不要提交到 Git,也不要直接复制到其他项目中使用。

模板在本地开发时会使用本地运行时模拟数据库和存储。邮件、Stripe、第三方登录等外部服务暂时不需要配置,等开发到对应功能时再填写相关凭据。

页面能够正常打开,只代表项目已经在本地成功运行,并不表示这些外部服务已经配置完成。

更改网站文案信息

初始化模板之后,先不用管主题样式,也不用管页面内容,首要任务是确认网站的三要素:域名、标题和描述。

domain: webpagetopdf.dev
title: Webpage to PDF
description: Convert a URL to PDF online. Capture full webpages, customize PDF layouts, and hide unwanted content with a visual editor before exporting.

接下来不需要手动修改,只需要在 Codex 中执行项目自带的 Agent 即可完成内容初始化,提示词如下:

使用以下参数执行 $saavo-initialize-content:

{
  "domain": "webpagetopdf.dev",
  "title": "Webpage to PDF",
  "description": "Convert a URL to PDF online. Capture full webpages, customize PDF layouts, and hide unwanted content with a visual editor before exporting."
}

执行完成后,再打开 config/base.ts 文件,确认里面的内容已经更新为:

export const websiteBaseCfg = {
    siteName: {
        value: "Webpage to PDF",
        key: 'website.title',
    },

    siteDescription: {
        value: "Convert a URL to PDF online. Capture full webpages, customize PDF layouts, and hide unwanted content with a visual editor before exporting.",
        key: 'website.description',
    },

    siteOGImage: '',
    siteOGImageAlt: '',
    siteOGImageWidth: 1200,
    siteOGImageHeight: 630,

    twitterSite: '',
    twitterCreator: '',
    twitterImage: '',

    fbAppId: '',

    // 精确匹配的允许列表。应用验证该地址后才授予管理员角色。
    // 这些值有意不进行邮箱别名匹配。
    adminEmails: ['admin@saavo.dev'],
    supportEmail: 'support@saavo.dev',
    fromEmailAddress: {
        name: "Webpage to PDF",
        email: 'send@mail.saavo.dev',
    },
}

顺手把邮箱配置也更新一下:

export const websiteBaseCfg = {
    ...
    adminEmails: ['admin@webpagetopdf.dev'],
    supportEmail: 'support@webpagetopdf.dev',
    fromEmailAddress: {
        name: "Webpage to PDF",
        email: 'send@mail.webpagetopdf.dev',
    },
}

接下来最好在 Codex 中再检查一遍,避免 Agent 执行过程中存在遗漏:

根据上面的 Webpage to PDF 产品说明,独立检查网站,直接修正已经确认的遗漏、旧品牌内容、不一致的文案、翻译问题和意外修改。不要依赖之前的报告,也不要调用初始化技能。避免无关修改,执行相关检查,并简要说明已修正的内容和剩余问题。

确认无误后,重新运行 npm run dev 确认网站信息已经更新。

本地运行截图

调整主题颜色

运行上面的初始化 Agent 后,网页中的文案已经更改为新的内容,但是主题颜色还是默认的,接下来我们调整一下主题颜色。

主题颜色只需要调整项目中的 styles/base.css 文件,不需要手动逐项修改,直接让 Codex 处理即可。

找到之前的原型项目,让 Codex 按照固定格式提取调色盘信息:

提取当前原型的配色,填入下方 CSS 模板。

要求:
1. 优先使用源代码中的实际颜色。如果只有截图,则从截图提取颜色。保留原型的品牌特征和视觉风格。
2. 如果原型没有深色主题,创建中性的炭灰色主题。背景、卡片和侧边栏保持中性色,品牌色主要用于按钮和强调元素。
3. 计算对比度:普通文字(包括次要文字)与其背景的对比度至少为 4.5:1。必要的控件边界和焦点指示器与相邻颜色的对比度至少为 3:1,必要时调整颜色。
4. 只替换 <HEX> 和 <SHADOW>。保留选择器、变量名、顺序、分组及其他所有设置。使用小写六位 HEX 颜色,阴影颜色使用 rgb(R G B / A)。
5. 只输出完整的 CSS。不要遗漏变量、保留占位符或修改项目文件。

:root {
  color-scheme: light;
  --radius: 0.75rem;

  --background: <HEX>;
  --foreground: <HEX>;
  --card: <HEX>;
  --card-foreground: <HEX>;
  --popover: <HEX>;
  --popover-foreground: <HEX>;
  --primary: <HEX>;
  --primary-foreground: <HEX>;
  --secondary: <HEX>;
  --secondary-foreground: <HEX>;
  --muted: <HEX>;
  --muted-foreground: <HEX>;
  --accent: <HEX>;
  --accent-foreground: <HEX>;
  --destructive: <HEX>;
  --destructive-foreground: <HEX>;
  --border: <HEX>;
  --input: <HEX>;
  --ring: <HEX>;

  --brand-primary: <HEX>;
  --brand-primary-strong: <HEX>;
  --brand-warm: <HEX>;
  --brand-success: <HEX>;
  --brand-danger: <HEX>;
  --brand-info: <HEX>;
  --brand-from: <HEX>;
  --brand-to: <HEX>;

  --chart-1: <HEX>;
  --chart-2: <HEX>;
  --chart-3: <HEX>;
  --chart-4: <HEX>;
  --chart-5: <HEX>;

  --sidebar: <HEX>;
  --sidebar-foreground: <HEX>;
  --sidebar-primary: <HEX>;
  --sidebar-primary-foreground: <HEX>;
  --sidebar-accent: <HEX>;
  --sidebar-accent-foreground: <HEX>;
  --sidebar-border: <HEX>;
  --sidebar-ring: <HEX>;

  --shadow-xs-value: <SHADOW>;
  --shadow-sm-value: <SHADOW>;
  --shadow-md-value: <SHADOW>;
  --shadow-lg-value: <SHADOW>;
}

.dark {
  color-scheme: dark;

  --background: <HEX>;
  --foreground: <HEX>;
  --card: <HEX>;
  --card-foreground: <HEX>;
  --popover: <HEX>;
  --popover-foreground: <HEX>;
  --primary: <HEX>;
  --primary-foreground: <HEX>;
  --secondary: <HEX>;
  --secondary-foreground: <HEX>;
  --muted: <HEX>;
  --muted-foreground: <HEX>;
  --accent: <HEX>;
  --accent-foreground: <HEX>;
  --destructive: <HEX>;
  --destructive-foreground: <HEX>;
  --border: <HEX>;
  --input: <HEX>;
  --ring: <HEX>;

  --brand-primary: <HEX>;
  --brand-primary-strong: <HEX>;
  --brand-warm: <HEX>;
  --brand-success: <HEX>;
  --brand-danger: <HEX>;
  --brand-info: <HEX>;
  --brand-from: <HEX>;
  --brand-to: <HEX>;

  --chart-1: <HEX>;
  --chart-2: <HEX>;
  --chart-3: <HEX>;
  --chart-4: <HEX>;
  --chart-5: <HEX>;

  --sidebar: <HEX>;
  --sidebar-foreground: <HEX>;
  --sidebar-primary: <HEX>;
  --sidebar-primary-foreground: <HEX>;
  --sidebar-accent: <HEX>;
  --sidebar-accent-foreground: <HEX>;
  --sidebar-border: <HEX>;
  --sidebar-ring: <HEX>;

  --shadow-xs-value: <SHADOW>;
  --shadow-sm-value: <SHADOW>;
  --shadow-md-value: <SHADOW>;
  --shadow-lg-value: <SHADOW>;
}

然后将提取到的信息填入到 styles/base.css 文件中即可。

调整主题颜色

本篇检查

完成这一篇后,应能在本地打开项目,并确认网站文案、邮箱配置和主题颜色已经更新。

教程总览 · 上一篇:产品设计与原型验证 · 下一篇:迁移 PDF 转换功能