Raycast:生产力 UI 的艺术

Raycast如何实现低于50ms的响应时间并保持个性:macOS原生设计、键盘优先、扩展生态系统和HUD模式。包含Swift实现模式。

5 分钟阅读 325 字
Raycast:生产力 UI 的艺术 screenshot

Raycast:效率工具界面的艺术

"我们相信最好的工具应该让你感觉不到它的存在。" — Raycast 团队

Raycast 是一款 macOS 启动器,已成为专业用户替代 Spotlight 的首选。它展示了如何在追求极致效率的同时,保持产品的温度和个性。


Raycast 的重要意义

Raycast 证明了实用型软件不必给人冰冷的感觉。它完美结合了: - 极速响应(启动时间 <50ms) - 键盘优先的交互方式 - 通过商店实现的可扩展性 - 通过精致细节展现的产品个性

核心成就: - 重新定义了启动器的可能性 - 证明了开发者工具也可以拥有个性 - 打造了一个繁荣的扩展生态系统 - 树立了 macOS 原生设计的新标杆


核心要点

  1. 50ms 是速度的临界值 - 用户能感知到任何超过 50ms 的延迟;这应该被视为硬性工程约束,而非指导方针
  2. 原生平台集成建立信任感 - 毛玻璃效果、SF Symbols、系统强调色和标准快捷键让 Raycast 感觉就像 macOS 的一部分
  3. 键盘快捷键必须可被发现 - 在结果列表中直接显示快捷键(⌘1、⌘2),而不是隐藏在提示框或文档中
  4. 个性不应牺牲性能 - 成就达成时的彩带特效、趣味性的空状态、机智的文案都能带来愉悦感,同时不增加延迟
  5. 扩展生态需要设计语言 - 第三方扩展之所以感觉原生,是因为它们使用相同的表单组件、操作面板和导航模式

核心设计原则

1. 即时响应

Raycast 感觉就像是你思维的延伸。没有延迟,无需等待。

实现方式: - 原生 Swift 实现(而非 Electron) - 预加载的扩展索引 - 激进的缓存策略 - 优化的渲染管线

50ms 法则: 如果某个操作超过 50ms,用户就会注意到。Raycast 将此视为硬性约束。

设计启示: - 立即显示 UI,异步加载数据 - 为耗时操作提供骨架屏状态 - 永远不要阻塞主线程 - 优先本地处理,减少网络调用

// Raycast-style instant response pattern
struct SearchView: View {
    @State private var results: [Result] = []
    @State private var isLoading = false

    var body: some View {
        VStack {
            // Show immediately with cached data
            ForEach(cachedResults) { result in
                ResultRow(result)
            }

            if isLoading {
                // Unobtrusive loading indicator
                ProgressView()
                    .scaleEffect(0.5)
            }
        }
        .task(id: query) {
            isLoading = true
            results = await search(query)
            isLoading = false
        }
    }
}

2. 深度 macOS 集成

Raycast 让人感觉它天生就属于 macOS。它尊重平台惯例,同时不断突破边界。

原生行为: - 毛玻璃和模糊效果(NSVisualEffectView) - 系统强调色 - 全面使用 SF Symbols - 原生键盘快捷键 - 响应系统深色/浅色模式

实现方式:

// macOS vibrancy
struct RaycastWindow: View {
    var body: some View {
        VStack {
            // Content
        }
        .background(.ultraThinMaterial)  // Native blur
        .clipShape(RoundedRectangle(cornerRadius: 12, style: .continuous))
    }
}

// System accent color
Text("Selected")
    .foregroundStyle(.tint)  // Follows system preference

// SF Symbols with semantic colors
Image(systemName: "checkmark.circle.fill")
    .foregroundStyle(.green)

窗口设计:

┌────────────────────────────────────────────────────────────┐
│ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │
│ ░░                                                      ░░ │
│ ░░  > Search Raycast...                                 ░░ │
│ ░░                                                      ░░ │
│ ░░  ┌────────────────────────────────────────────────┐  ░░ │
│ ░░  │ [A]  Applications                      Cmd+1   │  ░░ │
│ ░░  │ [F]  File Search                       Cmd+2   │  ░░ │
│ ░░  │ [C]  Clipboard History                 Cmd+3   │  ░░ │
│ ░░  │ [S]  System Commands                   Cmd+4   │  ░░ │
│ ░░  └────────────────────────────────────────────────┘  ░░ │
│ ░░                                                      ░░ │
│ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │
└────────────────────────────────────────────────────────────┘
                    ↑ 毛玻璃效果让桌面透过窗口显现

3. 卓越的键盘体验与视觉反馈

每个操作都有键盘快捷键,每个快捷键都有视觉确认。

快捷键显示模式:

RESULTS LIST:
┌────────────────────────────────────────────────────────────┐
│ Recent Applications                                        │
│                                                            │
│  (*)  Visual Studio Code                         Cmd+1     │
│       ~/Projects/my-app                                    │
│                                                            │
│  ( )  Figma                                      Cmd+2     │
│       ~/Design/project.fig                                 │
│                                                            │
│  ( )  Terminal                                   Cmd+3     │
│                                                            │
│ ───────────────────────────────────────────────────────────│
│  Actions                            Cmd+K  or  ->  to see  │
└────────────────────────────────────────────────────────────┘

操作面板:

┌────────────────────────────────────────────────────────────┐
│ Actions for "Visual Studio Code"                  esc <-   │
│                                                            │
│  Open                                             Enter    │
│  Open in New Window                           Cmd+Enter    │
│  ──────────────────────────────────────────────────────── │
│  Show in Finder                             Cmd+Shift+F    │
│  Copy Path                                  Cmd+Shift+C    │
│  ──────────────────────────────────────────────────────── │
│  Move to Trash                           Cmd+Backspace     │
└────────────────────────────────────────────────────────────┘

设计原则: - 内联显示快捷键(而非放在提示框中) - 按修饰键分组快捷键 - 在用户预期的地方使用标准 macOS 快捷键 - 采用助记模式(⌘⇧F = Finder)


4. 细节中的个性

Raycast 高效但不冷漠。微小的惊喜让它令人难忘。

示例: - 达成成就时的彩带特效 - 操作时的微妙音效 - 趣味性的空状态 - 为专业用户准备的彩蛋

空状态示例:

┌────────────────────────────────────────────────────────────┐
│                                                            │
│                        [?]                                 │
│                                                            │
│               No results for "asdfgh"                      │
│                                                            │
│          Try searching for something else,                 │
│          or check out the Extension Store                  │
│                                                            │
│              [Browse Extensions]                           │
│                                                            │
└────────────────────────────────────────────────────────────┘

彩带特效模式(用于成就达成):

// After completing onboarding, earning badge, etc.
ConfettiView()
    .transition(.opacity)
    .animation(.spring(), value: showConfetti)

个性化指南: - 愉悦感不应带来延迟 - 庆祝应该是赢得的(而非随机出现) - 在文案中注入个性("未找到相关结果……" vs "错误:结果为0") - 音效应该是可选的且不打扰用户


5. 扩展生态系统设计

Raycast 的扩展商店设计精美。

扩展卡片:

┌────────────────────────────────────────────────────────────┐
│  ┌──────┐                                                  │
│  │ [+]  │  GitHub                                          │
│  │      │  Search repos, PRs, and issues                   │
│  └──────┘                                                  │
│           ★★★★★  1.2k ratings  •  By Raycast               │
│                                                            │
│           [Install]                                        │
└────────────────────────────────────────────────────────────┘

扩展详情页:

┌────────────────────────────────────────────────────────────┐
│                                                            │
│  ← GitHub                                     [Uninstall]  │
│                                                            │
│  ┌──────────────────────────────────────────────────────┐ │
│  │                                                       │ │
│  │           [Screenshot of extension in use]           │ │
│  │                                                       │ │
│  └──────────────────────────────────────────────────────┘ │
│                                                            │
│  Commands                                                  │
│  ├─ Search Repositories                                    │
│  ├─ My Pull Requests                                       │
│  ├─ Search Issues                                          │
│  └─ Create Issue                                           │
│                                                            │
│  Changelog                                                 │
│  ├─ v2.1.0  Added organization filtering                   │
│  └─ v2.0.0  Complete rewrite with new API                  │
│                                                            │
└────────────────────────────────────────────────────────────┘

6. 扩展的表单设计

扩展使用统一的表单系统。

表单模式:

┌────────────────────────────────────────────────────────────┐
│ Create GitHub Issue                                   esc  │
│                                                            │
│  Repository                                                │
│  ┌──────────────────────────────────────────────────────┐ │
│  │ raycast/extensions                               ▾   │ │
│  └──────────────────────────────────────────────────────┘ │
│                                                            │
│  Title                                                     │
│  ┌──────────────────────────────────────────────────────┐ │
│  │ Bug: Search not working                              │ │
│  └──────────────────────────────────────────────────────┘ │
│                                                            │
│  Description                                               │
│  ┌──────────────────────────────────────────────────────┐ │
│  │ When I search for...                                 │ │
│  │                                                       │ │
│  │                                                       │ │
│  └──────────────────────────────────────────────────────┘ │
│                                                            │
│  Labels                                    (optional)      │
│  ┌──────────────────────────────────────────────────────┐ │
│  │ [bug] [needs-triage]                             +   │ │
│  └──────────────────────────────────────────────────────┘ │
│                                                            │
│           [Create Issue ⌘↵]    [Cancel esc]                │
│                                                            │
└────────────────────────────────────────────────────────────┘

表单组件样式:

// Raycast-style form in SwiftUI
Form {
    Section("Repository") {
        Picker("", selection: $repo) {
            ForEach(repos) { repo in
                Text(repo.name).tag(repo)
            }
        }
        .labelsHidden()
    }

    Section("Title") {
        TextField("Bug: ...", text: $title)
    }

    Section("Description") {
        TextEditor(text: $description)
            .frame(minHeight: 100)
    }
}
.formStyle(.grouped)

值得借鉴的设计模式

搜索优先模式

一切从搜索开始。

┌────────────────────────────────────────────────────────────┐
│  > |                                                       │
│    ^ 启动后光标立即定位于此                                   │
│                                                            │
│  输入以搜索命令、应用、文件等内容                              │
└────────────────────────────────────────────────────────────┘

实现要点: - 启动时自动聚焦搜索框 - 模糊匹配并高亮显示 - 智能排序(最近使用 + 使用频率 + 相关性) - 边输入边显示结果

层级导航

深层功能,但不增加复杂度。

第 1 层:主搜索
    ↓ Enter
第 2 层:扩展命令
    ↓ Enter
第 3 层:操作结果
    ↓ ⌘K
第 4 层:操作面板

始终:esc 返回上一层

HUD 确认提示

快速反馈,不打断工作流。

操作完成后:
┌────────────────────────┐
│  ✓ 已复制到剪贴板       │
└────────────────────────┘
    ↑ 短暂出现后淡出
      无需手动关闭
// HUD pattern
struct HUDView: View {
    @State private var show = false

    var body: some View {
        if show {
            Text("✓ Copied")
                .padding(.horizontal, 16)
                .padding(.vertical, 8)
                .background(.regularMaterial)
                .cornerRadius(8)
                .transition(.opacity.combined(with: .scale))
                .onAppear {
                    DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
                        withAnimation { show = false }
                    }
                }
        }
    }
}

可以从 Raycast 借鉴的设计

启动器/搜索界面

  1. 50ms 或更快 - 速度是不可妥协的底线
  2. 随处可见的快捷键 - 可发现的,而非隐藏的
  3. 带高亮的模糊搜索 - 展示结果匹配的原因
  4. 操作面板 (⌘K) - 上下文相关的操作
  5. HUD 确认提示 - 快速、非阻塞的反馈

macOS 应用

  1. 原生毛玻璃效果 - 使用 NSVisualEffectView/.material
  2. SF Symbols - 与系统保持一致
  3. 系统颜色 - 遵循用户的强调色偏好
  4. 连续圆角 - .cornerRadius(style: .continuous)
  5. 标准快捷键 - 不要重新发明 ⌘C、⌘V 等

具体技术

技术 应用方式
即时启动 预热窗口,延迟加载内容
模糊搜索 使用 Fuse.js 或类似库
快捷键提示 在结果中内联显示 ⌘1、⌘2 等
操作面板 通过 ⌘K 或 → 触发次要操作
HUD 反馈 Toast 样式,自动消失
扩展 API 清晰的契约,一致的 UI

色彩系统

Raycast 使用与系统绑定的语义化颜色。

// Raycast-inspired semantic colors
extension Color {
    static let rayBackground = Color(nsColor: .windowBackgroundColor)
    static let raySecondary = Color.secondary
    static let rayTertiary = Color(nsColor: .tertiaryLabelColor)

    // Status colors
    static let raySuccess = Color.green
    static let rayWarning = Color.orange
    static let rayError = Color.red

    // Accent follows system
    static let rayAccent = Color.accentColor
}

// Dark mode example
struct ResultRow: View {
    var body: some View {
        HStack {
            Image(systemName: "app.fill")
                .foregroundStyle(.secondary)

            Text("Application Name")
                .foregroundStyle(.primary)

            Spacer()

            Text("⌘ 1")
                .font(.caption)
                .foregroundStyle(.tertiary)
                .padding(.horizontal, 6)
                .padding(.vertical, 2)
                .background(Color.secondary.opacity(0.2))
                .cornerRadius(4)
        }
        .padding(.horizontal, 12)
        .padding(.vertical, 8)
    }
}

核心洞察

"每一毫秒都至关重要。用户能感知到 50ms 和 100ms 之间的差异。"

"键盘快捷键应该是可发现的,而不是需要从文档中记忆的。"

"个性让软件令人难忘,效率让软件不可或缺。"

"扩展应该感觉像原生功能,而不是外挂附加物。相同的 UI 语言,相同的交互方式。"


常见问题

Raycast 如何实现低于 50ms 的响应时间?

Raycast 使用原生 Swift 实现(而非 Electron),在启动时预加载扩展索引,积极缓存搜索结果,并优化渲染管线。UI 会立即显示,同时数据在后台异步加载。50ms 阈值被视为硬性约束,而非目标。

为什么 Raycast 看起来如此原生于 macOS?

Raycast 使用 NSVisualEffectView 实现毛玻璃效果,所有图标使用 SF Symbols,系统强调色会跟随用户偏好设置,并采用标准的 macOS 键盘快捷键。窗口使用连续圆角,并自动跟随系统的深色/浅色模式。

Raycast 对键盘快捷键的设计理念是什么?

每个操作都有键盘快捷键,直接显示在结果列表中(⌘1、⌘2 等),而不是隐藏在工具提示里。快捷键采用助记模式(⌘⇧F 打开 Finder,⌘⇧C 复制路径)。操作面板(⌘K)提供次级操作及其对应的快捷键。

Raycast 如何在不影响速度的情况下增添个性?

庆祝动效是需要赢得的,而且很短暂——彩纸效果只在达成成就后出现,而不是随机触发。空状态有俏皮的文案("没有找到相关结果……"而不是"错误:0 条结果")。声音是可选的且不会打扰用户。愉悦感永远不会阻塞用户或增加延迟。

Raycast 的扩展系统如何保持一致性?

所有扩展使用相同的 UI 组件(表单、列表、操作面板),遵循相同的导航模式(Enter 键进入下一级,Escape 键返回上一级),并继承系统样式。API 强制保证一致性,使第三方扩展与内置功能难以区分。


资源