mac 效率工具:Raycast 的扩展开发

🎯 Raycast 简介

Raycast 是一款专为 macOS 设计的现代化生产力工具,类似于 Alfred 和 Spotlight 的替代品。与前辈们相比,它更现代化,更符合现代使用和开发习惯。它采用 TypeScript + React 技术栈,为前端开发者提供了优秀(爽歪歪)的扩展开发体验。

核心特点

  • 现代化 UI: 原生级别的用户界面体验
  • TypeScript 支持: 完整的类型定义和开发体验
  • React 组件: 使用 React 构建复杂的用户界面
  • AI 集成: 内置 AI 能力支持
  • 丰富的 API: 提供大量原生功能接口

🛠️ 开发环境准备

1. 安装 Raycast

bash 复制代码
# 通过 Homebrew 安装
brew install --cask raycast

# 或直接下载安装包
# 访问 https://www.raycast.com 下载最新版本

2. 安装开发工具(开发文档:developers.raycast.com/)

bash 复制代码
# 安装 Node.js (建议 v18 或更高版本)
# 安装 npm 或 yarn 或其他工具
npm install -g npm@latest

# 安装 Raycast CLI 工具
npm install -g @raycast/cli

3. 开发环境验证

bash 复制代码
# 检查 Raycast CLI 是否安装成功
raycast --version

# 查看可用命令
raycast --help

🚀 创建扩展项目

1. 初始化项目

bash 复制代码
# 创建新的扩展项目
raycast create my-extension

# 进入项目目录
cd my-extension

# 安装依赖
npm install

2. 项目结构解析

perl 复制代码
my-extension/
├── package.json          # 项目配置和依赖
├── tsconfig.json         # TypeScript 配置
├── raycast-env.d.ts      # Raycast 类型定义
├── src/
│   ├── index.tsx         # 主入口文件
│   ├── search.tsx        # 搜索命令组件
│   └── preferences.tsx   # 偏好设置
├── assets/
│   ├── icon.png         # 扩展图标
│   └── screenshots/     # 截图资源
├── metadata/
│   └── raycast.png      # Raycast 商店展示图
└── README.md            # 项目说明文档

3. 核心文件说明

package.json

json 复制代码
{
  "name": "my-extension",
  "title": "My Extension",
  "description": "描述你的扩展功能",
  "icon": "icon.png",
  "author": "your-name",
  "license": "MIT",
  "commands": [
    {
      "name": "search",
      "title": "Search Command",
      "description": "搜索功能的描述",
      "mode": "view"
    }
  ],
  "dependencies": {
    "@raycast/api": "^1.0.0",
    "@raycast/utils": "^1.0.0"
  }
}

src/index.tsx (基础模板)

typescript 复制代码
import { List, ActionPanel, Action, showToast, Toast } from "@raycast/api";
import { useState, useEffect } from "react";

interface SearchResult {
  id: string;
  title: string;
  description: string;
}

export default function Command() {
  const [searchResults, setSearchResults] = useState<SearchResult[]>([]);
  const [isLoading, setIsLoading] = useState(false);

  useEffect(() => {
    // 初始化数据加载
    loadData();
  }, []);

  async function loadData() {
    try {
      setIsLoading(true);
      // 这里添加你的数据获取逻辑
      const results = await fetchData();
      setSearchResults(results);
    } catch (error) {
      showToast({
        style: Toast.Style.Failure,
        title: "加载失败",
        message: error instanceof Error ? error.message : "未知错误"
      });
    } finally {
      setIsLoading(false);
    }
  }

  async function fetchData(): Promise<SearchResult[]> {
    // 模拟数据获取
    return [
      { id: "1", title: "结果 1", description: "描述信息" },
      { id: "2", title: "结果 2", description: "另一个描述" }
    ];
  }

  return (
    <List isLoading={isLoading} searchBarPlaceholder="搜索...">
      {searchResults.map((result) => (
        <List.Item
          key={result.id}
          title={result.title}
          subtitle={result.description}
          actions={
            <ActionPanel>
              <Action
                title="执行操作"
                onAction={() => {
                  showToast({
                    style: Toast.Style.Success,
                    title: "操作完成",
                    message: `执行了: ${result.title}`
                  });
                }}
              />
            </ActionPanel>
          }
        />
      ))}
    </List>
  );
}

📋 开发核心概念

1. 命令 (Commands)

命令是 Raycast 扩展的核心组件,每个扩展可以包含多个命令。

命令类型(mode)

  • view: 显示用户界面 (默认)
  • no-view: 后台执行,无界面
  • menu-bar: 菜单栏扩展

命令配置示例

json 复制代码
{
  "commands": [
    {
      "name": "search",
      "title": "搜索",
      "description": "执行搜索操作",
      "mode": "view",
      "preferences": [
        {
          "name": "apiKey",
          "type": "password",
          "title": "API 密钥",
          "description": "用于API调用的密钥"
        }
      ]
    }
  ]
}

2. 组件 (Components)

Raycast 提供了丰富的 UI 组件:

基础组件

typescript 复制代码
import { 
  List, 
  Detail, 
  Form,
  ActionPanel, 
  Action,
  Icon,
  Color,
  showToast,
  Toast,
  confirmAlert,
  Alert
} from "@raycast/api";

常用组件示例

List 组件

typescript 复制代码
<List
  searchBarPlaceholder="搜索项目..."
  isLoading={isLoading}
  onSearchTextChange={setSearchText}
  throttle
>
  <List.Section title="分类 1">
    <List.Item
      title="项目标题"
      subtitle="副标题"
      icon={Icon.Star}
      accessories={[
        { text: "标签", icon: Icon.Tag }
      ]}
      actions={
        <ActionPanel>
          <Action.OpenInBrowser url="https://example.com" />
          <Action.CopyToClipboard content="复制内容" />
        </ActionPanel>
      }
    />
  </List.Section>
</List>

Detail 组件

typescript 复制代码
<Detail
  markdown={markdownContent}
  navigationTitle="详情页面"
  actions={
    <ActionPanel>
      <Action
        title="刷新"
        icon={Icon.ArrowClockwise}
        onAction={handleRefresh}
      />
    </ActionPanel>
  }
/>

Form 组件

typescript 复制代码
<Form
  actions={
    <ActionPanel>
      <Action.SubmitForm title="提交" onSubmit={handleSubmit} />
    </ActionPanel>
  }
>
  <Form.TextField
    id="title"
    title="标题"
    placeholder="输入标题"
    defaultValue="默认标题"
  />
  <Form.TextArea
    id="description"
    title="描述"
    placeholder="输入描述"
  />
  <Form.Checkbox
    id="enabled"
    label="启用"
    defaultValue={true}
  />
</Form>

3. 钩子 (Hooks)

Raycast 提供了实用的 React Hooks:

typescript 复制代码
import { 
  usePromise,
  useCachedState,
  useFetch,
  useFrecencySorting,
  useAI
} from "@raycast/utils";

// 异步数据加载
const { isLoading, data, error } = usePromise(fetchData, [query]);

// 本地存储
const [items, setItems] = useCachedState<Item[]>("cached-items", []);

// HTTP 请求
const { data, isLoading, error } = useFetch("https://api.example.com/data");

// AI 集成
const { data, isLoading } = useAI("请总结这段文本", { 
  creativity: "medium" 
});

4. 偏好设置 (Preferences)

typescript 复制代码
import { getPreferenceValues } from "@raycast/api";

interface Preferences {
  apiKey: string;
  defaultLanguage: string;
  maxResults: number;
}

const preferences = getPreferenceValues<Preferences>();

🔧 高级功能开发

1. 网络请求

typescript 复制代码
import { useFetch } from "@raycast/utils";
import { showToast, Toast } from "@raycast/api";

interface ApiResponse {
  data: any[];
  total: number;
}

export default function Command() {
  const { data, isLoading, error } = useFetch<ApiResponse>(
    "https://api.example.com/items",
    {
      headers: {
        "Authorization": `Bearer ${preferences.apiKey}`,
        "Content-Type": "application/json"
      },
      onError: (error) => {
        showToast({
          style: Toast.Style.Failure,
          title: "API 请求失败",
          message: error.message
        });
      }
    }
  );

  // 使用获取的数据...
}

2. 本地存储

typescript 复制代码
import { LocalStorage } from "@raycast/api";

// 存储数据
await LocalStorage.setItem("user-preferences", JSON.stringify(preferences));

// 读取数据
const stored = await LocalStorage.getItem("user-preferences");
const prefs = stored ? JSON.parse(stored) : {};

// 删除数据
await LocalStorage.removeItem("user-preferences");

// 清空所有存储
await LocalStorage.clear();

3. 剪贴板操作

typescript 复制代码
import { Clipboard, showHUD } from "@raycast/api";

// 读取剪贴板
const text = await Clipboard.readText();

// 写入剪贴板
await Clipboard.copy("要复制的内容");

// 复制并显示提示
await Clipboard.copy("已复制到剪贴板");
await showHUD("已复制到剪贴板");

// 复制富文本
await Clipboard.copy({ text: "纯文本", html: "<b>HTML</b>内容" });

4. 文件系统操作

typescript 复制代码
import { environment, showInFinder } from "@raycast/api";
import { execSync } from "child_process";

// 获取支持目录路径
const supportPath = environment.supportPath;
const cachePath = environment.cachePath;

// 打开文件
await showInFinder("/path/to/file");

// 执行系统命令
try {
  const result = execSync("ls -la", { encoding: "utf-8" });
  console.log(result);
} catch (error) {
  console.error("命令执行失败:", error);
}

🧪 调试和测试

1. 开发模式

bash 复制代码
# 启动开发模式
npm run dev

# 在 Raycast 中重新加载扩展
# 使用快捷键 Cmd+R 重新加载

2. 日志输出

typescript 复制代码
// 控制台日志
console.log("调试信息");
console.error("错误信息");
console.warn("警告信息");

// 显示调试信息
showToast({
  style: Toast.Style.Success,
  title: "调试信息",
  message: JSON.stringify(data, null, 2)
});

3. VSCode 调试配置

.vscode/launch.json 中添加:

json 复制代码
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Debug Raycast Extension",
      "type": "node",
      "request": "launch",
      "runtimeExecutable": "raycast",
      "runtimeArgs": ["debug"],
      "env": {
        "NODE_ENV": "development"
      },
      "console": "integratedTerminal"
    }
  ]
}

4. 错误处理最佳实践

typescript 复制代码
import { showToast, Toast, Alert, confirmAlert } from "@raycast/api";

// 统一的错误处理
async function handleError(error: unknown, context: string) {
  const message = error instanceof Error ? error.message : "未知错误";
  
  console.error(`[${context}]`, error);
  
  await showToast({
    style: Toast.Style.Failure,
    title: "操作失败",
    message: `${context}: ${message}`
  });
}

// 用户确认对话框
async function confirmAction(message: string): Promise<boolean> {
  return await confirmAlert({
    title: "确认操作",
    message: message,
    primaryAction: {
      title: "确认",
      style: Alert.ActionStyle.Default
    },
    dismissAction: {
      title: "取消",
      style: Alert.ActionStyle.Cancel
    }
  });
}

📦 扩展打包和发布

1. 构建扩展

bash 复制代码
# 构建生产版本
npm run build

# 验证构建结果
npm run lint
npm run test

2. 本地测试

bash 复制代码
# 在 Raycast 中导入本地扩展
# 1. 打开 Raycast
# 2. 输入 "Import Extension"
# 3. 选择扩展目录

3. 发布到 Raycast Store

准备发布材料

markdown 复制代码
# README.md 模板

## 扩展名称

扩展的简要描述

### 功能特性
- 功能1
- 功能2
- 功能3

### 安装
1. 安装扩展
2. 配置偏好设置
3. 开始使用

### 配置
- API 密钥: 在偏好设置中配置
- 语言设置: 支持多语言

### 截图
![功能截图](screenshots/main.png)

### 更新日志
#### v1.0.0
- 初始版本发布

发布流程

  1. 创建 GitHub 仓库
bash 复制代码
# 初始化 git 仓库
git init
git add .
git commit -m "Initial commit"
git remote add origin https://github.com/yourusername/my-extension.git
git push -u origin main
  1. 提交到 Raycast Store
bash 复制代码
# 使用 Raycast CLI 提交
raycast publish

# 或者通过 GitHub 提交 PR
# 1. Fork raycast/extensions 仓库
# 2. 创建新分支
# 3. 添加扩展目录到 extensions/ 目录下
# 4. 提交 Pull Request
  1. 等待审核
  • Raycast 团队会审核你的扩展
  • 通常需要 3-7 个工作日
  • 审核通过后会自动发布到 Store

4. 发布要求和最佳实践

技术要求

  • 使用 TypeScript 开发
  • 遵循 Raycast 编码规范
  • 包含完整的错误处理
  • 提供清晰的文档

用户体验要求

  • 响应式设计
  • 加载状态处理
  • 错误提示友好
  • 快捷键支持

代码质量要求

typescript 复制代码
// 良好的代码结构
import { useState, useEffect } from "react";
import { List, ActionPanel, Action, showToast, Toast } from "@raycast/api";
import { usePromise, useCachedState } from "@raycast/utils";

interface Item {
  id: string;
  title: string;
  description: string;
}

export default function Command() {
  const [searchText, setSearchText] = useState("");
  const [cachedItems, setCachedItems] = useCachedState<Item[]>("items", []);
  
  const { isLoading, data, error } = usePromise(
    async (query: string) => {
      // 数据获取逻辑
      return await fetchItems(query);
    },
    [searchText]
  );

  // 错误处理
  useEffect(() => {
    if (error) {
      showToast({
        style: Toast.Style.Failure,
        title: "获取数据失败",
        message: error.message
      });
    }
  }, [error]);

  return (
    <List
      isLoading={isLoading}
      searchText={searchText}
      onSearchTextChange={setSearchText}
      searchBarPlaceholder="搜索项目..."
    >
      {data?.map((item) => (
        <List.Item
          key={item.id}
          title={item.title}
          subtitle={item.description}
          actions={
            <ActionPanel>
              <Action
                title="查看详情"
                onAction={() => {/* 处理逻辑 */}}
              />
            </ActionPanel>
          }
        />
      ))}
    </List>
  );
}

🔄 版本管理和更新

1. 版本号规范

遵循语义化版本控制 (SemVer):

  • 主版本号 (MAJOR): 不兼容的 API 修改
  • 次版本号 (MINOR): 向下兼容的功能性新增
  • 修订号 (PATCH): 向下兼容的问题修正

2. 更新流程

bash 复制代码
# 1. 更新版本号
npm version patch  # 或 minor, major

# 2. 更新 CHANGELOG.md
# 3. 提交更改
git add .
git commit -m "Release v1.1.0"
git push origin main

# 4. 重新发布
raycast publish

3. 更新日志模板

markdown 复制代码
# Changelog

## [1.1.0] - 2025-10-12

### 新增
- 功能描述

### 修复
- 修复问题描述

### 改进
- 改进描述

📚 常用资源和工具

1. 官方资源

2. 开发工具

  • VSCode 扩展: Raycast Extension
  • 调试工具: React Developer Tools
  • 图标资源: SF Symbols, Feather Icons

3. 社区资源

💡 一些调试技巧

typescript 复制代码
// 开发模式下的调试信息
if (process.env.NODE_ENV === "development") {
  console.log("调试信息:", data);
}

// 使用 Raycast 的调试工具
import { environment } from "@raycast/api";

console.log("运行环境:", environment);
console.log("支持路径:", environment.supportPath);

🎯 开发流程总结

  1. 环境准备: 安装 Raycast 和开发工具
  2. 项目创建: 使用 CLI 创建扩展项目
  3. 功能开发: 实现核心功能逻辑
  4. 界面设计: 使用 Raycast 组件构建 UI
  5. 测试调试: 本地测试和调试
  6. 打包发布: 构建并发布到 Store
  7. 维护更新: 持续改进和维护

本文章基于 Raycast 最新版本编写,随着平台更新,部分功能可能会有所变化,建议定期查看官方文档获取最新信息。

相关推荐
white-persist2 小时前
XXE 注入漏洞全解析:从原理到实战
开发语言·前端·网络·安全·web安全·网络安全·信息可视化
练习时长一年2 小时前
Spring内置功能
java·前端·spring
SHUIPING_YANG3 小时前
完美迁移:将 nvm 和 npm 完全安装到 Windows D 盘
前端·windows·npm
lypzcgf3 小时前
Coze源码分析-资源库-编辑数据库-前端源码-核心组件
前端·数据库·源码分析·coze·coze源码分析·ai应用平台·agent平台
勤奋菲菲3 小时前
Koa.js 完全指南:下一代 Node.js Web 框架
前端·javascript·node.js
晒太阳5794 小时前
懒加载与按需加载
前端
10年前端老司机4 小时前
面试官爱问的 Object.defineProperty,90%的人倒在这些细节上!
前端·javascript
庞囧4 小时前
从输入 URL 到开始解析 HTML 之间:浏览器背后发生了什么
前端