vibes

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
Plan mode: If you are planning work, this entire skill is ONE plan step: "Invoke /vibes:vibes". Do not decompose the steps below into separate plan tasks.
Display this ASCII art immediately when starting:
░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░▒▓███████▓▒░░▒▓████████▓▒░░▒▓███████▓▒░
░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░      ░▒▓█▓▒░
 ░▒▓█▓▒▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░      ░▒▓█▓▒░
 ░▒▓█▓▒▒▓█▓▒░░▒▓█▓▒░▒▓███████▓▒░░▒▓██████▓▒░  ░▒▓██████▓▒░
  ░▒▓█▓▓█▓▒░ ░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░             ░▒▓█▓▒░
  ░▒▓█▓▓█▓▒░ ░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░             ░▒▓█▓▒░
   ░▒▓██▓▒░  ░▒▓█▓▒░▒▓███████▓▒░░▒▓████████▓▒░▒▓███████▓▒░
规划模式:如果您正在规划工作,整个技能是一个规划步骤:"Invoke /vibes:vibes"。请勿将以下步骤拆分为单独的规划任务。
启动时立即显示此ASCII艺术字:
░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░▒▓███████▓▒░░▒▓████████▓▒░░▒▓███████▓▒░
░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░      ░▒▓█▓▒░
 ░▒▓█▓▒▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░      ░▒▓█▓▒░
 ░▒▓█▓▒▒▓█▓▒░░▒▓█▓▒░▒▓███████▓▒░░▒▓██████▓▒░  ░▒▓██████▓▒░
  ░▒▓█▓▓█▓▒░ ░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░             ░▒▓█▓▒░
  ░▒▓█▓▓█▓▒░ ░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░             ░▒▓█▓▒░
   ░▒▓██▓▒░  ░▒▓█▓▒░▒▓███████▓▒░░▒▓████████▓▒░▒▓███████▓▒░

Quick Navigation

快速导航



Vibes DIY App Generator

Vibes DIY应用生成器

Generate React web applications using Fireproof for local-first data persistence.
使用Fireproof生成支持本地优先数据持久化的React Web应用。

Pre-Flight Check: Connect Status

飞行前检查:Connect状态

MANDATORY: Complete these steps BEFORE generating any app code.
Step 0: Check Connect Status
Run this command first to validate all required credentials:
bash
if test -f "./.env" && \
   grep -qE "^VITE_CLERK_PUBLISHABLE_KEY=pk_(test|live)_" ./.env 2>/dev/null && \
   grep -qE "^VITE_API_URL=" ./.env 2>/dev/null && \
   grep -qE "^VITE_CLOUD_URL=" ./.env 2>/dev/null; then
  echo "CONNECT_READY"
else
  echo "CONNECT_NOT_READY"
fi
If output is "CONNECT_NOT_READY", Connect setup is required:
Connect with Clerk authentication is required for Vibes apps.
Invoke
/vibes:connect
to deploy Connect, then return here when complete.
If Connect IS set up (CONNECT_READY), proceed directly to app generation. The assemble script will populate Connect config from .env.
Platform Name vs User Intent: "Vibes" is the name of this app platform (Vibes DIY). When users say "vibe" or "vibes" in their prompt, interpret it as:
  • Their project/brand name ("my vibes tracker")
  • A positive descriptor ("good vibes app")
  • NOT as "mood/atmosphere" literally
Do not default to ambient mood generators, floating orbs, or meditation apps unless explicitly requested.
Import Map Note: The import map points
use-fireproof
to
/fireproof-vibes-bridge.js
, a bridge module that wraps the raw Fireproof bundle with sync status forwarding and an onTock kick effect. Your code uses
import { useFireproofClerk } from "use-fireproof"
and the browser resolves this through the bridge →
./fireproof-clerk-bundle.js
. This is intentional—the bridge ensures
useLiveQuery
subscribers see synced data and that
SyncStatusDot
gets live sync status via a window global.
必填:在生成任何应用代码前完成以下步骤。
步骤0:检查Connect状态
首先运行此命令以验证所有必需的凭据:
bash
if test -f "./.env" && \
   grep -qE "^VITE_CLERK_PUBLISHABLE_KEY=pk_(test|live)_" ./.env 2>/dev/null && \
   grep -qE "^VITE_API_URL=" ./.env 2>/dev/null && \
   grep -qE "^VITE_CLOUD_URL=" ./.env 2>/dev/null; then
  echo "CONNECT_READY"
else
  echo "CONNECT_NOT_READY"
fi
如果输出为"CONNECT_NOT_READY",则需要设置Connect:
Vibes应用需要搭配Clerk身份验证的Connect。
调用
/vibes:connect
部署Connect,完成后返回此处。
如果Connect已设置完成(CONNECT_READY),直接进入应用生成。组装脚本会从.env文件中填充Connect配置。
平台名称与用户意图:"Vibes"是此应用平台的名称(Vibes DIY)。当用户在提示中提到"vibe"或"vibes"时,将其解读为:
  • 他们的项目/品牌名称(如"my vibes tracker")
  • 积极的描述词(如"good vibes app")
  • 不要字面理解为"情绪/氛围"
除非用户明确要求,否则不要默认生成氛围情绪生成器、悬浮球体或冥想类应用。
导入映射说明:导入映射将
use-fireproof
指向
/fireproof-vibes-bridge.js
,这是一个桥接模块,它包装了原始Fireproof包,实现同步状态转发和onTock触发效果。您的代码使用
import { useFireproofClerk } from "use-fireproof"
,浏览器会通过桥接模块解析到
./fireproof-clerk-bundle.js
。这是有意设计的——桥接模块确保
useLiveQuery
订阅者能看到同步后的数据,并且
SyncStatusDot
能通过全局window对象获取实时同步状态。

Core Rules

核心规则

  • Use JSX - Standard React syntax with Babel transpilation
  • Single HTML file - App code assembled into template
  • Fireproof for data - Use
    useFireproofClerk
    for database + sync
  • Auto-detect Connect - Template handles Clerk auth when Connect is configured
  • Tailwind for styling - Mobile-first, responsive design
  • 使用JSX - 标准React语法,支持Babel转译
  • 单HTML文件 - 应用代码组装到模板中
  • Fireproof处理数据 - 使用
    useFireproofClerk
    实现数据库+同步
  • 自动检测Connect - 模板在配置Connect后自动处理Clerk身份验证
  • Tailwind进行样式设计 - 移动端优先的响应式设计

Generation Process

生成流程

Step 1: Design Reasoning

步骤1:设计推理

Before writing code, reason about the design in
<design>
tags:
<design>
- What is the core functionality and user flow?
- What OKLCH colors fit this theme? (dark/light, warm/cool, vibrant/muted)
- What layout best serves the content? (cards, list, dashboard, single-focus)
- What micro-interactions would feel satisfying? (hover states, transitions)
- What visual style matches the purpose? (minimal, bold, playful, professional)
</design>
Assembly: generate (preserve)
assemble.js
injects your code as-is. Import and export statements work because the import map intercepts bare specifiers at runtime. Code examples below include imports.
If you're a launch/builder agent: Sell transforms vibes artifacts by stripping imports. When generating app.jsx for the launch pipeline, omit all imports — the sell template provides everything. Follow builder.md rules; use only the patterns from examples below, not the import lines.
编写代码前,在
<design>
标签中进行设计推理:
<design>
- 核心功能和用户流程是什么?
- 哪些OKLCH颜色适合此主题?(深色/浅色、暖色/冷色、鲜艳/柔和)
- 哪种布局最适合内容展示?(卡片、列表、仪表板、单焦点)
- 哪些微交互会带来愉悦感?(悬停状态、过渡动画)
- 哪种视觉风格符合应用目的?(极简、大胆、趣味、专业)
</design>
组装:生成(保留)
assemble.js
会按原样注入您的代码。导入和导出语句可以正常工作,因为导入映射会在运行时拦截裸模块说明符。下面的代码示例包含导入语句。
如果您是启动/构建代理:通过移除导入语句来优化Vibes产物。为启动流水线生成app.jsx时,省略所有导入——售卖模板会提供所有依赖。遵循builder.md规则;仅使用以下示例中的模式,不要使用导入行。

Step 2: Output Code

步骤2:输出代码

After reasoning, output the complete JSX in
<code>
tags:
<code>
import React, { useState } from "react";
import { useFireproofClerk } from "use-fireproof";

export default function App() {
  const { database, useLiveQuery, useDocument, syncStatus } = useFireproofClerk("app-name-db");
  // ... component logic

  return (
    <div className="min-h-screen bg-[#f1f5f9] p-4">
      {/* Sync status indicator (optional) */}
      <div className="text-xs text-gray-500 mb-2">Sync: {syncStatus}</div>
      {/* Your app UI */}
    </div>
  );
}
</code>
⚠️ CRITICAL: Fireproof Hook Pattern
The
@necrodome/fireproof-clerk
package exports ONLY
useFireproofClerk
. Always use this pattern:
jsx
// ✅ CORRECT - This is the ONLY pattern that works
import { useFireproofClerk } from "use-fireproof";
const { database, useDocument, useLiveQuery, syncStatus } = useFireproofClerk("my-db");
const { doc, merge } = useDocument({ _id: "doc1" });

// ❌ WRONG - DO NOT USE (old use-vibes API)
import { toCloud, useFireproof } from "use-fireproof";  // WRONG - old API
import { useDocument } from "use-fireproof";  // WRONG - standalone import
const { attach } = useFireproof("db", { attach: toCloud() });  // WRONG - old pattern
Sync Status:
syncStatus
provides the current sync state. Values:
"idle"
,
"connecting"
,
"synced"
,
"reconnecting"
,
"error"
. Display it for user feedback.
Connect Configuration: Generated apps require Clerk authentication and cloud sync. The
assemble.js
script populates
window.__VIBES_CONFIG__
from your
.env
file. Apps will show a configuration error if credentials are missing.
推理完成后,在
<code>
标签中输出完整的JSX代码:
<code>
import React, { useState } from "react";
import { useFireproofClerk } from "use-fireproof";

export default function App() {
  const { database, useLiveQuery, useDocument, syncStatus } = useFireproofClerk("app-name-db");
  // ... 组件逻辑

  return (
    <div className="min-h-screen bg-[#f1f5f9] p-4">
      {/* 同步状态指示器(可选) */}
      <div className="text-xs text-gray-500 mb-2">Sync: {syncStatus}</div>
      {/* 您的应用UI */}
    </div>
  );
}
</code>
⚠️ 重要:Fireproof钩子模式
@necrodome/fireproof-clerk
包仅导出
useFireproofClerk
。请始终使用以下模式:
jsx
// ✅ 正确 - 这是唯一有效的模式
import { useFireproofClerk } from "use-fireproof";
const { database, useDocument, useLiveQuery, syncStatus } = useFireproofClerk("my-db");
const { doc, merge } = useDocument({ _id: "doc1" });

// ❌ 错误 - 请勿使用(旧版use-vibes API)
import { toCloud, useFireproof } from "use-fireproof";  // 错误 - 旧版API
import { useDocument } from "use-fireproof";  // 错误 - 独立导入
const { attach } = useFireproof("db", { attach: toCloud() });  // 错误 - 旧模式
同步状态
syncStatus
提供当前的同步状态,取值包括:
"idle"
"connecting"
"synced"
"reconnecting"
"error"
。建议显示此状态以提供用户反馈。
Connect配置:生成的应用需要Clerk身份验证和云同步。
assemble.js
脚本会从您的.env文件中填充
window.__VIBES_CONFIG__
。如果缺少凭据,应用会显示配置错误。

Assembly Workflow

组装工作流

  1. Extract the code from
    <code>
    tags and write to
    app.jsx
  2. Optionally save
    <design>
    content to
    design.md
    for documentation
  3. Run assembly:
    bash
    node "${CLAUDE_PLUGIN_ROOT}/scripts/assemble.js" app.jsx index.html
  4. Deploy the app so the user can see it. Clerk auth requires a public URL — the app cannot be viewed locally. Auto-invoke /vibes:cloudflare to deploy, then present the live URL.

⚠️ DEPRECATED API: Never use the old
useFireproof
with
toCloud()
pattern. See references/DEPRECATED.md for migration details if you encounter legacy code.

  1. <code>
    标签中提取代码并写入
    app.jsx
  2. (可选)将
    <design>
    内容保存到
    design.md
    作为文档
  3. 运行组装命令:
    bash
    node "${CLAUDE_PLUGIN_ROOT}/scripts/assemble.js" app.jsx index.html
  4. 部署应用以便用户查看。Clerk身份验证需要公共URL——应用无法在本地查看。自动调用
    /vibes:cloudflare
    进行部署,然后提供实时URL。

⚠️ 已废弃API: 请勿使用旧版
useFireproof
搭配
toCloud()
的模式。如果遇到遗留代码,请参考references/DEPRECATED.md了解迁移细节。

UI Style & Theming

UI样式与主题

OKLCH Colors (Recommended)

推荐使用OKLCH颜色

Use OKLCH for predictable, vibrant colors. Unlike RGB/HSL, OKLCH has perceptual lightness - changing L by 10% looks the same across all hues.
css
oklch(L C H)
/* L = Lightness (0-1): 0 black, 1 white */
/* C = Chroma (0-0.4): 0 gray, higher = more saturated */
/* H = Hue (0-360): color wheel degrees */
Theme-appropriate palettes:
jsx
{/* Dark/moody theme */}
className="bg-[oklch(0.15_0.02_250)]"  /* Deep blue-black */

{/* Warm/cozy theme */}
className="bg-[oklch(0.25_0.08_30)]"   /* Warm brown */

{/* Fresh/bright theme */}
className="bg-[oklch(0.95_0.03_150)]"  /* Mint white */

{/* Vibrant accent */}
className="bg-[oklch(0.7_0.2_145)]"    /* Vivid green */
使用OKLCH实现可预测的鲜艳颜色。与RGB/HSL不同,OKLCH具有感知亮度——将L值改变10%在所有色调中视觉效果一致。
css
oklch(L C H)
/* L = 亮度 (0-1): 0为黑色,1为白色 */
/* C = 色度 (0-0.4): 0为灰色,值越高饱和度越高 */
/* H = 色相 (0-360): 色轮度数 */
主题适配调色板:
jsx
{/* 深色/氛围感主题 */}
className="bg-[oklch(0.15_0.02_250)]"  /* 深蓝黑色 */

{/* 温暖/舒适主题 */}
className="bg-[oklch(0.25_0.08_30)]"   /* 暖棕色 */

{/* 清新/明亮主题 */}
className="bg-[oklch(0.95_0.03_150)]"  /* 薄荷白 */

{/* 鲜艳强调色 */}
className="bg-[oklch(0.7_0.2_145)]"    /* 鲜绿色 */

Better Gradients with OKLCH

使用OKLCH实现更平滑的渐变

Use
in oklch
for smooth gradients without muddy middle zones:
jsx
{/* Smooth gradient - no gray middle */}
className="bg-[linear-gradient(in_oklch,oklch(0.6_0.2_250),oklch(0.6_0.2_150))]"

{/* Sunset gradient */}
className="bg-[linear-gradient(135deg_in_oklch,oklch(0.7_0.25_30),oklch(0.5_0.2_330))]"

{/* Dark glass effect */}
className="bg-[linear-gradient(180deg_in_oklch,oklch(0.2_0.05_270),oklch(0.1_0.02_250))]"
使用
in oklch
实现无浑浊中间色的平滑渐变:
jsx
{/* 平滑渐变 - 无灰色中间色 */}
className="bg-[linear-gradient(in_oklch,oklch(0.6_0.2_250),oklch(0.6_0.2_150))]"

{/* 日落渐变 */}
className="bg-[linear-gradient(135deg_in_oklch,oklch(0.7_0.25_30),oklch(0.5_0.2_330))]"

{/* 深色毛玻璃效果 */}
className="bg-[linear-gradient(180deg_in_oklch,oklch(0.2_0.05_270),oklch(0.1_0.02_250))]"

Neobrute Style (Optional)

Neobrute风格(可选)

For bold, graphic UI:
  • Borders: thick 4px, dark
    border-[#0f172a]
  • Shadows: hard offset
    shadow-[6px_6px_0px_#0f172a]
  • Corners: square (0px) OR pill (rounded-full) - no in-between
jsx
<button className="px-6 py-3 bg-[oklch(0.95_0.02_90)] border-4 border-[#0f172a] shadow-[6px_6px_0px_#0f172a] hover:shadow-[4px_4px_0px_#0f172a] font-bold">
  Click Me
</button>
用于大胆的图形化UI:
  • 边框:4px粗边框,深色
    border-[#0f172a]
  • 阴影:硬偏移
    shadow-[6px_6px_0px_#0f172a]
  • 圆角:方形(0px)或药丸形(rounded-full)- 不使用中间值
jsx
<button className="px-6 py-3 bg-[oklch(0.95_0.02_90)] border-4 border-[#0f172a] shadow-[6px_6px_0px_#0f172a] hover:shadow-[4px_4px_0px_#0f172a] font-bold">
  Click Me
</button>

Glass Morphism (Dark themes)

毛玻璃效果(深色主题)

jsx
<div className="bg-white/5 backdrop-blur-lg border border-white/10 rounded-2xl">
  {/* content */}
</div>
jsx
<div className="bg-white/5 backdrop-blur-lg border border-white/10 rounded-2xl">
  {/* 内容 */}
</div>

Color Modifications

颜色调整

Lighten/darken using L value:
  • Hover: increase L by 0.05-0.1
  • Active/pressed: decrease L by 0.05
  • Disabled: reduce C to near 0

通过L值调整明暗:
  • 悬停状态:L值增加0.05-0.1
  • 激活/按下状态:L值减少0.05
  • 禁用状态:C值降至接近0

Fireproof API

Fireproof API

Fireproof is a local-first database - no loading or error states required, just empty data states. Data persists across sessions and syncs in real-time when Connect is configured.
Fireproof是本地优先数据库——无需加载或错误状态,只需处理空数据状态。数据在会话间持久化,配置Connect后可实现实时同步。

Setup

设置

jsx
import { useFireproofClerk } from "use-fireproof";

const { database, useLiveQuery, useDocument, syncStatus } = useFireproofClerk("my-app-db");
Note: When Connect is configured (via .env), the template wraps your App in
ClerkFireproofProvider
, enabling authenticated cloud sync automatically. Your code just uses
useFireproofClerk
.
jsx
import { useFireproofClerk } from "use-fireproof";

const { database, useLiveQuery, useDocument, syncStatus } = useFireproofClerk("my-app-db");
注意:当配置Connect后(通过.env),模板会将您的App包裹在
ClerkFireproofProvider
中,自动启用带身份验证的云同步。您的代码只需使用
useFireproofClerk
即可。

Choosing Your Pattern

选择合适的模式

useDocument = Form-like editing. Accumulate changes with
merge()
, then save with
submit()
or
save()
. Best for: text inputs, multi-field forms, editing workflows.
database.put() + useLiveQuery = Immediate state changes. Each action writes directly. Best for: counters, toggles, buttons, any single-action updates.
jsx
// FORM PATTERN: User types, then submits
const { doc, merge, submit } = useDocument({ title: "", body: "", type: "post" });
// merge({ title: "..." }) on each keystroke, submit() when done

// IMMEDIATE PATTERN: Each click is a complete action
const { docs } = useLiveQuery("_id", { key: "counter" });
const count = docs[0]?.value || 0;
const increment = () => database.put({ _id: "counter", value: count + 1 });
WARNING — merge() + submit() timing trap: Never
merge()
a computed value (like
Date.now()
or
crypto.randomUUID()
) and call
submit()
in the same event handler. React batches state updates, so
submit()
reads stale state and the merged field may be missing from the saved document. Use
database.put()
with explicit fields instead:
jsx
// BAD — ts may not be saved due to React batching
merge({ ts: Date.now() });
submit();

// GOOD — all fields written atomically
await database.put({ text: doc.text, ts: Date.now(), type: "item" });
reset();
useDocument = 类表单编辑。使用
merge()
累积更改,然后使用
submit()
save()
保存。最适合:文本输入、多字段表单、编辑工作流。
database.put() + useLiveQuery = 即时状态更改。每个操作直接写入数据库。最适合:计数器、开关、按钮、任何单操作更新。
jsx
// 表单模式:用户输入,然后提交
const { doc, merge, submit } = useDocument({ title: "", body: "", type: "post" });
// 每次按键时调用merge({ title: "..." }),完成后调用submit()

// 即时模式:每次点击都是完整操作
const { docs } = useLiveQuery("_id", { key: "counter" });
const count = docs[0]?.value || 0;
const increment = () => database.put({ _id: "counter", value: count + 1 });
警告 — merge() + submit()时序陷阱: 切勿在同一事件处理程序中
merge()
计算值(如
Date.now()
crypto.randomUUID()
)并调用
submit()
。React会批量处理状态更新,因此
submit()
会读取旧状态,导致合并的字段可能不会被保存到文档中。请改用
database.put()
并传入显式字段:
jsx
// 错误 — 由于React批处理,ts可能不会被保存
merge({ ts: Date.now() });
submit();

// 正确 — 所有字段原子性写入
await database.put({ text: doc.text, ts: Date.now(), type: "item" });
reset();

useDocument - Form State (NOT useState)

useDocument - 表单状态(请勿使用useState)

IMPORTANT: Don't use
useState()
for form data. Use
merge()
and
submit()
from
useDocument
. Only use
useState
for ephemeral UI state (active tabs, open/closed panels).
jsx
// Create new documents (auto-generated _id recommended)
const { doc, merge, submit, reset } = useDocument({ text: "", type: "item" });

// Edit existing document by known _id
const { doc, merge, save } = useDocument({ _id: "user-profile:abc@example.com" });

// Methods:
// - merge(updates) - update fields: merge({ text: "new value" })
// - submit(e) - save + reset (for forms creating new items)
// - save() - save without reset (for editing existing items)
// - reset() - discard changes
重要:请勿使用
useState()
处理表单数据。请使用
useDocument
提供的
merge()
submit()
。仅将
useState
用于临时UI状态(如活动标签页、面板展开/关闭)。
jsx
// 创建新文档(推荐自动生成_id)
const { doc, merge, submit, reset } = useDocument({ text: "", type: "item" });

// 通过已知_id编辑现有文档
const { doc, merge, save } = useDocument({ _id: "user-profile:abc@example.com" });

// 方法:
// - merge(updates) - 更新字段:merge({ text: "new value" })
// - submit(e) - 保存+重置(适用于创建新项的表单)
// - save() - 保存但不重置(适用于编辑现有项)
// - reset() - 丢弃更改

useLiveQuery - Real-time Lists

useLiveQuery - 实时列表

jsx
// Simple: query by field value
const { docs } = useLiveQuery("type", { key: "item" });

// Recent items (_id is roughly temporal - great for simple sorting)
const { docs } = useLiveQuery("_id", { descending: true, limit: 100 });

// Range query
const { docs } = useLiveQuery("rating", { range: [3, 5] });
CRITICAL: Custom index functions are SANDBOXED and CANNOT access external variables. Query all, filter in render:
jsx
// GOOD: Query all, filter in render
const { docs: allItems } = useLiveQuery("type", { key: "item" });
const filtered = allItems.filter(d => d.category === selectedCategory);
jsx
// 简单查询:按字段值查询
const { docs } = useLiveQuery("type", { key: "item" });

// 最新项(_id大致按时间排序 - 非常适合简单排序)
const { docs } = useLiveQuery("_id", { descending: true, limit: 100 });

// 范围查询
const { docs } = useLiveQuery("rating", { range: [3, 5] });
关键:自定义索引函数运行在沙箱中,无法访问外部变量。请查询所有数据,在渲染时进行过滤:
jsx
// 正确:查询所有数据,渲染时过滤
const { docs: allItems } = useLiveQuery("type", { key: "item" });
const filtered = allItems.filter(d => d.category === selectedCategory);

Direct Database Operations

直接数据库操作

jsx
// Create/update
const { id } = await database.put({ text: "hello", type: "item" });

// Delete
await database.del(item._id);
jsx
// 创建/更新
const { id } = await database.put({ text: "hello", type: "item" });

// 删除
await database.del(item._id);

Common Pattern - Form + List

常见模式 - 表单+列表

jsx
import React from "react";
import { useFireproofClerk } from "use-fireproof";

export default function App() {
  const { database, useLiveQuery, useDocument, syncStatus } = useFireproofClerk("my-db");

  // Form for new items (submit resets for next entry)
  const { doc, merge, submit } = useDocument({ text: "", type: "item" });

  // Live list of all items of type "item"
  const { docs } = useLiveQuery("type", { key: "item" });

  return (
    <div className="min-h-screen bg-[#f1f5f9] p-4">
      {/* Optional sync status indicator */}
      <div className="text-xs text-gray-500 mb-2">Sync: {syncStatus}</div>
      <form onSubmit={submit} className="mb-4">
        <input
          value={doc.text}
          onChange={(e) => merge({ text: e.target.value })}
          className="w-full px-4 py-3 border-4 border-[#0f172a]"
        />
        <button type="submit" className="mt-2 px-4 py-2 bg-[#0f172a] text-[#f1f5f9]">
          Add
        </button>
      </form>
      {docs.map(item => (
        <div key={item._id} className="p-2 mb-2 bg-white border-4 border-[#0f172a]">
          {item.text}
          <button onClick={() => database.del(item._id)} className="ml-2 text-red-500">
            Delete
          </button>
        </div>
      ))}
    </div>
  );
}

jsx
import React from "react";
import { useFireproofClerk } from "use-fireproof";

export default function App() {
  const { database, useLiveQuery, useDocument, syncStatus } = useFireproofClerk("my-db");

  // 用于创建新项的表单(提交后重置以便输入下一项)
  const { doc, merge, submit } = useDocument({ text: "", type: "item" });

  // 所有类型为"item"的实时列表
  const { docs } = useLiveQuery("type", { key: "item" });

  return (
    <div className="min-h-screen bg-[#f1f5f9] p-4">
      {/* 可选同步状态指示器 */}
      <div className="text-xs text-gray-500 mb-2">Sync: {syncStatus}</div>
      <form onSubmit={submit} className="mb-4">
        <input
          value={doc.text}
          onChange={(e) => merge({ text: e.target.value })}
          className="w-full px-4 py-3 border-4 border-[#0f172a]"
        />
        <button type="submit" className="mt-2 px-4 py-2 bg-[#0f172a] text-[#f1f5f9]">
          Add
        </button>
      </form>
      {docs.map(item => (
        <div key={item._id} className="p-2 mb-2 bg-white border-4 border-[#0f172a]">
          {item.text}
          <button onClick={() => database.del(item._id)} className="ml-2 text-red-500">
            Delete
          </button>
        </div>
      ))}
    </div>
  );
}

AI Features (Optional)

AI功能(可选)

If the user's prompt suggests AI-powered features (chatbot, summarization, content generation, etc.), the app needs AI capabilities via the
useAI
hook.
如果用户提示中包含AI相关功能(聊天机器人、摘要、内容生成等),应用需要通过
useAI
钩子添加AI能力。

Detecting AI Requirements

检测AI需求

Look for these patterns in the user's prompt:
  • "chatbot", "chat with AI", "ask AI"
  • "summarize", "generate", "write", "create content"
  • "analyze", "classify", "recommend"
  • "AI-powered", "intelligent", "smart" (in context of features)
在用户提示中寻找以下模式:
  • "chatbot"、"chat with AI"、"ask AI"
  • "summarize"、"generate"、"write"、"create content"
  • "analyze"、"classify"、"recommend"
  • "AI-powered"、"intelligent"、"smart"(在功能语境下)

Collecting OpenRouter Key

收集OpenRouter密钥

When AI is needed, ask the user:
This app needs AI capabilities. Please provide your OpenRouter API key. Get one at: https://openrouter.ai/keys
Store the key for use with the
--ai-key
flag during deployment.
当需要AI功能时,请向用户询问:
此应用需要AI能力。请提供您的OpenRouter API密钥。 获取地址:https://openrouter.ai/keys
存储密钥以便在部署时使用
--ai-key
标志。

Using the useAI Hook

使用useAI钩子

The
useAI
hook is automatically included in the template when AI features are detected:
jsx
import React from "react";
import { useFireproofClerk } from "use-fireproof";

export default function App() {
  const { database, useLiveQuery, syncStatus } = useFireproofClerk("ai-chat-db");
  const { callAI, loading, error } = useAI();

  const handleSend = async (message) => {
    // Save user message
    await database.put({ role: "user", content: message, type: "message" });

    // Call AI
    const response = await callAI({
      model: "anthropic/claude-sonnet-4",
      messages: [{ role: "user", content: message }]
    });

    // Save AI response
    const aiMessage = response.choices[0].message.content;
    await database.put({ role: "assistant", content: aiMessage, type: "message" });
  };

  // Handle limit exceeded
  if (error?.code === 'LIMIT_EXCEEDED') {
    return (
      <div className="p-4 bg-amber-100 text-amber-800 rounded">
        AI usage limit reached. Please wait for monthly reset or upgrade your plan.
      </div>
    );
  }

  // ... rest of UI
}
检测到AI功能需求时,模板会自动包含
useAI
钩子:
jsx
import React from "react";
import { useFireproofClerk } from "use-fireproof";

export default function App() {
  const { database, useLiveQuery, syncStatus } = useFireproofClerk("ai-chat-db");
  const { callAI, loading, error } = useAI();

  const handleSend = async (message) => {
    // 保存用户消息
    await database.put({ role: "user", content: message, type: "message" });

    // 调用AI
    const response = await callAI({
      model: "anthropic/claude-sonnet-4",
      messages: [{ role: "user", content: message }]
    });

    // 保存AI响应
    const aiMessage = response.choices[0].message.content;
    await database.put({ role: "assistant", content: aiMessage, type: "message" });
  };

  // 处理超出限制的情况
  if (error?.code === 'LIMIT_EXCEEDED') {
    return (
      <div className="p-4 bg-amber-100 text-amber-800 rounded">
        AI使用额度已达上限。请等待每月重置或升级您的计划。
      </div>
    );
  }

  // ... 其余UI代码
}

useAI API

useAI API

jsx
const { callAI, loading, error, clearError } = useAI();

// callAI options
await callAI({
  model: "anthropic/claude-sonnet-4",  // or other OpenRouter models
  messages: [
    { role: "system", content: "You are a helpful assistant." },
    { role: "user", content: "Hello!" }
  ],
  temperature: 0.7,  // optional
  max_tokens: 1000   // optional
});

// error structure
error = {
  code: "LIMIT_EXCEEDED" | "API_ERROR" | "NETWORK_ERROR",
  message: "Human-readable error message"
}
jsx
const { callAI, loading, error, clearError } = useAI();

// callAI选项
await callAI({
  model: "anthropic/claude-sonnet-4",  // 或其他OpenRouter模型
  messages: [
    { role: "system", content: "You are a helpful assistant." },
    { role: "user", content: "Hello!" }
  ],
  temperature: 0.7,  // 可选
  max_tokens: 1000   // 可选
});

// 错误结构
error = {
  code: "LIMIT_EXCEEDED" | "API_ERROR" | "NETWORK_ERROR",
  message: "人类可读的错误消息"
}

Deployment with AI

部署AI应用

When deploying AI-enabled apps, include the OpenRouter key:
bash
node "${CLAUDE_PLUGIN_ROOT}/scripts/deploy-cloudflare.js" \
  --name myapp \
  --file index.html \
  --ai-key "sk-or-v1-your-key"

部署支持AI的应用时,请包含OpenRouter密钥:
bash
node "${CLAUDE_PLUGIN_ROOT}/scripts/deploy-cloudflare.js" \
  --name myapp \
  --file index.html \
  --ai-key "sk-or-v1-your-key"

Sharing / Inviting Users

分享/邀请用户

The template includes a built-in invite UI in VibesPanel (the slide-out menu). For custom sharing in user app code, use the
useSharing
hook:
javascript
const { inviteUser, listInvites, deleteInvite, findUser, ready } = window.useSharing();

// Invite by email
async function handleInvite(email) {
  if (!ready) return;
  const result = await inviteUser(email, 'read'); // 'read' or 'write'
  console.log('Invited:', result);
}
The hook is available on
window.useSharing
after Clerk loads. Check
ready
before calling methods.

模板在VibesPanel(侧边滑出菜单)中内置了邀请UI。如需在用户应用代码中实现自定义分享功能,请使用
useSharing
钩子:
javascript
const { inviteUser, listInvites, deleteInvite, findUser, ready } = window.useSharing();

// 通过邮箱邀请用户
async function handleInvite(email) {
  if (!ready) return;
  const result = await inviteUser(email, 'read'); // 'read' 或 'write'
  console.log('Invited:', result);
}
Clerk加载完成后,
useSharing
钩子会在
window.useSharing
上可用。调用方法前请检查
ready
状态。

Common Mistakes to Avoid

常见错误避免

  • DON'T use
    useState
    for form fields - use
    useDocument
  • DON'T use
    Fireproof.fireproof()
    - use
    useFireproofClerk()
    hook
  • DON'T use the old
    useFireproof
    with
    toCloud()
    - use
    useFireproofClerk
    instead
  • DON'T use white text on light backgrounds
  • DON'T use
    call-ai
    directly - use
    useAI
    hook instead (it handles proxying and limits)
  • DON'T use Fireproof's
    _files
    API for images — it has a sync bug where blobs arrive after metadata, causing 404s on other devices. Store image data as Uint8Array directly on documents:
    jsx
    // Convert file to Uint8Array (with resize)
    async function fileToImageData(file, maxDim = 1200) {
      const bitmap = await createImageBitmap(file);
      const scale = Math.min(1, maxDim / Math.max(bitmap.width, bitmap.height));
      const canvas = new OffscreenCanvas(bitmap.width * scale, bitmap.height * scale);
      canvas.getContext('2d').drawImage(bitmap, 0, 0, canvas.width, canvas.height);
      const blob = await canvas.convertToBlob({ type: 'image/jpeg', quality: 0.8 });
      return new Uint8Array(await blob.arrayBuffer());
    }
    
    // Display from Uint8Array
    function StoredImage({ data, type = 'image/jpeg', alt, className }) {
      const [url, setUrl] = useState(null);
      useEffect(() => {
        if (!data) return;
        // Fireproof CBOR round-trips Uint8Array as plain objects with numeric keys
        const bytes = data instanceof Uint8Array ? data : new Uint8Array(Object.values(data));
        const objectUrl = URL.createObjectURL(new Blob([bytes], { type }));
        setUrl(objectUrl);
        return () => URL.revokeObjectURL(objectUrl);
      }, [data, type]);
      return url ? <img src={url} alt={alt} className={className} /> : null;
    }
    // Usage: <StoredImage data={doc.imageData} type={doc.imageType} alt="Photo" />
  • DON'T call
    merge()
    and
    submit()
    in the same handler when adding computed fields (timestamps, UUIDs, derived values). React batches the state update from
    merge()
    , so
    submit()
    writes the old state. Use
    database.put()
    with explicit fields +
    reset()
    instead.
  • DON'T spread
    useDocument
    doc into
    database.put()
    — internal CRDT metadata contaminates the write and can corrupt the database (
    missing block
    errors). Build documents with explicit fields instead:
    jsx
    // BAD — spreads internal metadata
    await database.put({ ...doc, completed: true });
    
    // GOOD — explicit fields only
    await database.put({ _id: doc._id, type: doc.type, todo: doc.todo, completed: true });
  • DON'T wrap your app in
    VibeContextProvider
    - that's a vibes.diy platform-only component. Standalone apps use
    useFireproofClerk()
    directly.
  • DON'T panic if you see "Cannot read properties of null (reading 'useContext')" - the template already handles the React singleton via
    ?external=react,react-dom
    in the import map. Check that the import map wasn't accidentally modified.
  • NOTE: Apps use
    /fireproof-vibes-bridge.js
    — this bridge module wraps the local Fireproof bundle with sync status forwarding + onTock kick. The bundle itself (
    /fireproof-clerk-bundle.js
    ) is a temporary workaround that fixes a CID bug and includes sync improvements. Apps work correctly with it.
  • DON'T hand-write
    app.jsx
    and assemble it manually — always generate through
    /vibes:vibes
    , even for test or diagnostic apps. The skill generates code that's compatible with the template by construction. Hand-written code may include imports or patterns that conflict with the template's runtime setup.

  • 请勿使用
    useState
    处理表单字段 - 请使用
    useDocument
  • 请勿使用
    Fireproof.fireproof()
    - 请使用
    useFireproofClerk()
    钩子
  • 请勿使用旧版
    useFireproof
    搭配
    toCloud()
    - 请改用
    useFireproofClerk
  • 请勿在浅色背景上使用白色文本
  • 请勿直接使用
    call-ai
    - 请改用
    useAI
    钩子(它会处理代理和限制)
  • 请勿使用Fireproof的
    _files
    API处理图片——它存在同步bug,blob会在元数据之后到达,导致其他设备上出现404错误。 请将图片数据作为Uint8Array直接存储在文档中:
    jsx
    // 将文件转换为Uint8Array(支持缩放)
    async function fileToImageData(file, maxDim = 1200) {
      const bitmap = await createImageBitmap(file);
      const scale = Math.min(1, maxDim / Math.max(bitmap.width, bitmap.height));
      const canvas = new OffscreenCanvas(bitmap.width * scale, bitmap.height * scale);
      canvas.getContext('2d').drawImage(bitmap, 0, 0, canvas.width, canvas.height);
      const blob = await canvas.convertToBlob({ type: 'image/jpeg', quality: 0.8 });
      return new Uint8Array(await blob.arrayBuffer());
    }
    
    // 从Uint8Array显示图片
    function StoredImage({ data, type = 'image/jpeg', alt, className }) {
      const [url, setUrl] = useState(null);
      useEffect(() => {
        if (!data) return;
        // Fireproof CBOR会将Uint8Array转换为带数字键的普通对象
        const bytes = data instanceof Uint8Array ? data : new Uint8Array(Object.values(data));
        const objectUrl = URL.createObjectURL(new Blob([bytes], { type }));
        setUrl(objectUrl);
        return () => URL.revokeObjectURL(objectUrl);
      }, [data, type]);
      return url ? <img src={url} alt={alt} className={className} /> : null;
    }
    // 使用方式:<StoredImage data={doc.imageData} type={doc.imageType} alt="Photo" />
  • 请勿在同一处理程序中调用
    merge()
    submit()
    来添加计算字段 (时间戳、UUID、派生值)。React会批量处理
    merge()
    的状态更新,因此
    submit()
    会写入旧状态。请改用
    database.put()
    传入显式字段 +
    reset()
  • 请勿
    useDocument
    的doc对象展开到
    database.put()
    中——内部CRDT元数据 会污染写入操作,可能导致数据库损坏(出现
    missing block
    错误)。 请改用显式字段构建文档:
    jsx
    // 错误 — 展开了内部元数据
    await database.put({ ...doc, completed: true });
    
    // 正确 — 仅使用显式字段
    await database.put({ _id: doc._id, type: doc.type, todo: doc.todo, completed: true });
  • 请勿将您的应用包裹在
    VibeContextProvider
    中——这是vibes.diy平台专用组件。独立应用直接使用
    useFireproofClerk()
    即可。
  • 如果看到"Cannot read properties of null (reading 'useContext')"错误请不要惊慌——模板已通过导入映射中的
    ?external=react,react-dom
    处理React单例。请检查导入映射是否被意外修改。
  • 注意:应用使用
    /fireproof-vibes-bridge.js
    ——此桥接模块包装了本地Fireproof包,实现同步状态转发+onTock触发效果。包本身(
    /fireproof-clerk-bundle.js
    )是临时解决方案,修复了CID bug并包含同步改进。应用配合它可以正常工作。
  • 请勿手动编写
    app.jsx
    并手动组装——始终通过
    /vibes:vibes
    生成,即使是测试或诊断应用。该技能生成的代码 天生与模板兼容。手动编写的代码可能包含与模板运行时设置冲突的导入 或模式。

When to Read Extended Docs

何时阅读扩展文档

The shipped cache files contain detailed reference material. Read them when the user's prompt matches these signals:
NeedSignal in PromptRead This
File uploads"upload", "images", "photos", "attachments"
${CLAUDE_PLUGIN_ROOT}/docs/fireproof.txt
→ "Working with Images"
Auth / sync config"Clerk", "Connect", "cloud sync", "login"
${CLAUDE_PLUGIN_ROOT}/docs/fireproof.txt
→ "ClerkFireproofProvider Config"
Sync status display"online/offline", "connection status"
${CLAUDE_PLUGIN_ROOT}/docs/fireproof.txt
→ "Sync Status Display"
Full Neobrute design detailsdetailed design system, spacing, typography
${CLAUDE_PLUGIN_ROOT}/skills/vibes/cache/style-prompt.txt

随附的缓存文件包含详细的参考资料。当用户提示中出现以下信号时,请阅读这些资料:
需求提示中的信号阅读内容
文件上传"upload", "images", "photos", "attachments"
${CLAUDE_PLUGIN_ROOT}/docs/fireproof.txt
→ "Working with Images"
身份验证/同步配置"Clerk", "Connect", "cloud sync", "login"
${CLAUDE_PLUGIN_ROOT}/docs/fireproof.txt
→ "ClerkFireproofProvider Config"
同步状态显示"online/offline", "connection status"
${CLAUDE_PLUGIN_ROOT}/docs/fireproof.txt
→ "Sync Status Display"
完整Neobrute设计细节详细设计系统、间距、排版
${CLAUDE_PLUGIN_ROOT}/skills/vibes/cache/style-prompt.txt

Deployment Options

部署选项

After generating your app, deploy it:
  • Cloudflare - Edge deployment with Workers. Use
    /vibes:cloudflare
    to deploy.

生成应用后,进行部署:
  • Cloudflare - 基于边缘Workers的部署。使用
    /vibes:cloudflare
    进行部署。

What's Next?

下一步操作

After generating and assembling the app, present these options using AskUserQuestion:
Question: "Your app is live! Want to turn it into a product? The /sell skill adds multi-tenant SaaS with auth and billing. Or pick another direction:"
Header: "Next"
Options:
- Label: "Keep improving this app"
  Description: "Continue iterating on what you've built. Add new features, refine the styling, or adjust functionality. Great when you have a clear vision and want to polish it further."

- Label: "Apply a design reference (/design-reference)"
  Description: "Have a design.html or mockup file? This skill mechanically transforms your app to match it exactly - pixel-perfect fidelity with your Fireproof data binding preserved."

- Label: "Explore variations (/riff)"
  Description: "Not sure if this is the best approach? Riff generates 3-10 completely different interpretations of your idea in parallel. You'll get ranked variations with business model analysis to help you pick the winner."

- Label: "Make it a SaaS (/sell)"
  Description: "Ready to monetize? Sell transforms your app into a multi-tenant SaaS with Clerk authentication, subscription billing, and isolated databases per customer. Each user gets their own subdomain."

- Label: "Deploy to Cloudflare (/cloudflare)"
  Description: "Go live on the edge. Deploy to Cloudflare Workers with a subdomain registry, KV storage, and global CDN. Fast, scalable, and always on."

- Label: "I'm done for now"
  Description: "Wrap up this session. Your files are saved locally - come back anytime to continue."
After user responds:
  • "Keep improving" → Acknowledge and stay ready for iteration prompts. After each round of changes to app.jsx, re-run assembly and re-deploy.
  • "Apply a design reference" → Auto-invoke /vibes:design-reference skill
  • "Explore variations" → Auto-invoke /vibes:riff skill
  • "Make it a SaaS" → Auto-invoke /vibes:sell skill
  • "Deploy" → Auto-invoke /vibes:cloudflare skill
  • "I'm done" → Confirm files saved, wish them well
Do NOT proceed to code generation until: Connect setup is complete with valid Clerk credentials in .env (pre-flight check returns CONNECT_READY).
生成并组装应用后,使用AskUserQuestion呈现以下选项:
Question: "Your app is live! Want to turn it into a product? The /sell skill adds multi-tenant SaaS with auth and billing. Or pick another direction:"
Header: "Next"
Options:
- Label: "Keep improving this app"
  Description: "Continue iterating on what you've built. Add new features, refine the styling, or adjust functionality. Great when you have a clear vision and want to polish it further."

- Label: "Apply a design reference (/design-reference)"
  Description: "Have a design.html or mockup file? This skill mechanically transforms your app to match it exactly - pixel-perfect fidelity with your Fireproof data binding preserved."

- Label: "Explore variations (/riff)"
  Description: "Not sure if this is the best approach? Riff generates 3-10 completely different interpretations of your idea in parallel. You'll get ranked variations with business model analysis to help you pick the winner."

- Label: "Make it a SaaS (/sell)"
  Description: "Ready to monetize? Sell transforms your app into a multi-tenant SaaS with Clerk authentication, subscription billing, and isolated databases per customer. Each user gets their own subdomain."

- Label: "Deploy to Cloudflare (/cloudflare)"
  Description: "Go live on the edge. Deploy to Cloudflare Workers with a subdomain registry, KV storage, and global CDN. Fast, scalable, and always on."

- Label: "I'm done for now"
  Description: "Wrap up this session. Your files are saved locally - come back anytime to continue."
用户回应后:
  • "Keep improving" → 确认并准备好接收迭代提示。每次修改app.jsx后,重新运行组装并重新部署。
  • "Apply a design reference" → 自动调用
    /vibes:design-reference
    技能
  • "Explore variations" → 自动调用
    /vibes:riff
    技能
  • "Make it a SaaS" → 自动调用
    /vibes:sell
    技能
  • "Deploy" → 自动调用
    /vibes:cloudflare
    技能
  • "I'm done" → 确认文件已保存,祝用户愉快
在以下情况完成前请勿进行代码生成: Connect设置完成,.env文件中包含有效的Clerk凭据(飞行前检查返回CONNECT_READY)。