核心思想

Raycast 2.0 把整个应用从头重写以同时拿下 macOS 和 Windows。架构选型放弃 Electron/Tauri/全原生,自研「平台原生外壳 + 共享 React/TypeScript 前端 + Node 后台 + Rust 内核」的混合方案。真正的难点不在让 Raycast 跑起来,而在让它感觉对——花费大量工程力气绕开 WebKit 在节流、缩放、闪烁、emoji 渲染等场景的默认行为。代价是内存基线从 200–300 MB 涨到 350–450 MB;换来开发速度、跨平台覆盖与招聘宽度。

Raycast 跨平台重写背后的故事,以及那些让它感觉快、讨喜、熟悉的细节。

我们刚刚发布了 Raycast 2.0 的公测版。这是自 2020 年 Raycast 首次上线以来最大的一次更新,也是第一个同时跑在 macOS 和 Windows 上的版本。

Raycast 2.0 在 macOS 和 Windows 上的样子

为了做到这一点,我们把整个应用从头重写了一遍。换上了全新的架构,以及一套混合了 TypeScript、Swift、C#、Rust、Node 和 React 的技术栈。Web 技术从一开始就是 Raycast 的一部分,扩展系统和 Notes 都跑在它上面。到了 v2,我们加倍下注,同时让整个应用保持一如既往的原生感和速度。

如果说发布稿聊的是「新东西是什么」,这一篇要聊的是「它是怎么搭起来的」。这次重写背后的故事、一路上做出的那些选择,以及要完成这种规模的重写究竟意味着什么。难的不是让 Raycast 跑起来,难的是让它感觉对

我们的起点

Raycast v1 本质上是一个原生 macOS 应用,用 Swift 写在 AppKit 之上。我们几乎从不直接用标准 UI 控件 —— 它们不是为我们关心的那种键盘优先、重度用户的工作流而设计的,所以我们自己造。每一行列表项、每一个快捷键、每一个默认行为,都是我们自己处理的。SwiftUI 我们也用得不多。它和 Raycast 几乎是同步成熟的,但始终没有越过我们对性能和可控性的那条线。它在 v1 里唯一的栖身之地,是每年的 Wrapped 功能,而那部分和应用其他部分隔离得很彻底。

Raycast v1 的架构概览

扩展生态完全跑在另一套栈上。React、TypeScript 和 Node.js,UI 用声明式描述,由原生应用负责渲染。Felix 详细写过这套架构,可以读这篇。给第三方开发者选了一套熟悉的技术栈,是扩展商店今天能拥有数千个扩展、覆盖几乎所有工具的重要原因。这套 API 在设计上就考虑了可移植性 —— 一个扩展不会假设自己运行在 macOS 上,这也让我们去年得以把目录里很大一块扩展带上 Windows。

Raycast Notes 是我们第一次在应用的核心功能里使用 WebView。这个编辑器是一个 React 应用,跑在原生窗口承载的 WebView 里。我们想看看,能不能完全用 web 技术造出一个面板、又不破坏应用其他部分的手感。结果它跑通了 —— 如今 Notes 是非常多 macOS 和 iOS 用户每天都在用的功能。

虽然 v1 骨子里是个原生应用,但只要 web 技术是对的工具,我们一直都会拿来用。说到底,人们喜欢 Raycast 是因为它的手感,而不是因为底层用了什么

为什么要重写

2023 年底,我们开始认真考虑把 Raycast 带到 Windows 上。这一直都在计划里,从第一天就是,但早期我们想先把一个平台做对,再去想扩张的事。

那时候,Raycast 也已经从一个启动器,长成了一个更宽的生产力平台 —— AI Chat、Notes、扩展、同步、文件搜索等等。最初为启动器设计的架构,开始限制我们能往下做什么。编译时间在慢慢爬升,AppKit 越来越频繁地挡在路上,能深耕原生 macOS 的人也越来越难招。就算 Windows 不在桌面上,我们也得把这套东西重新想一遍。

于是我们开始为新的 Windows 客户端和现有的 macOS 客户端,一起挑选一套技术栈。但首先,这种规模的项目得有个好代号。我们叫它「X-Ray」—— cross-platform Raycast 的缩写。

选型这件事

我们先看了一圈 Windows 上可选的原生方案 —— 坦率地讲,Windows 原生 UI 框架的现状远算不上好。微软有「先推出一个 UI 框架,再转身忘记」的传统。WPF、UWP,以及现在的 WinUI 3 —— 后者还相当年轻,没经过大规模的实战检验。如果说在 macOS 上用 AppKit 造一个精致的原生应用已经够难,那在 Windows 上用 WinUI 3 做同样的事,风险要大得多。何况,一想到要同时维护两个独立的原生应用,我们就不太舒服 —— 毕竟 Raycast 大多数扩展应该在两个平台上长得一模一样。维护两套独立的 UI 栈,意味着双倍的工作量,却换不来更快的速度。

这让我们很快就排除了完全走原生这条路。而且 Raycast 的代码大部分是 UI,我们没法只共享一个后台、然后给每个平台搭独立的前端。于是我们看向 web 方案。它天然就是跨平台的,生态庞大,开发体验好,人才池也比原生桌面大几个数量级。Raycast 的扩展本来就跑在 web 栈上,而且效果一直不错,所以把它推广到整个应用上,感觉是一件很自然的事。即便我们只为 Windows 做选型,web 也是个合理的选项(微软自己也把混合应用列为桌面应用的推荐路径之一)。而它天然的跨平台属性,让 macOS 这边也值得考虑。

于是我们对三个方向做了评估:Electron、Tauri,以及自己造一套混合栈。

Electron 会是显而易见的选择。说实话,对大多数公司来说,要把一个桌面应用送到用户手里,它大概都是对的那个选择。它维护良好、久经考验,生态非常庞大。VS Code、Linear、Superhuman 这些应用都证明了,你完全可以用它造出优秀的产品。苹果和微软都没有为大团队在他们的平台上构建复杂桌面应用提供太多便利,而 Electron 正好填补了这块空白。老实讲,我们觉得这是件好事。

但具体到 Raycast,它不是最合的那一双鞋。Raycast 跟操作系统贴得很紧 —— 我们依赖全局快捷键、剪贴板管理、辅助功能 API、窗口管理、能浮在其他应用之上又不抢焦点的自定义面板,等等。我们需要直达底层原生代码,才能精细控制应用的每一个行为。哪怕是内部面板半透明这种小细节,对我们也至关重要。Electron 能做到其中一部分,但 web 和原生代码之间的边界用起来并不愉快。我们也不想在 macOS 上再捆绑一份 Chromium,因为系统的 WebKit 完全可以用。简单说,我们需要确保自己掌控这套栈的每一部分,并且在需要时能轻易回落到原生。在这件事上,Electron 不是最佳选择。

Tauri 有类似的局限。它在原生这边给到的控制更少,而且当时还足够年轻,不足以让我们把公司押上去。所以它也很快被排除了。

剩下的就是混合方案了。自己造原生外壳、把系统 WebView 包进去 —— 这套方案给到的恰好是我们需要的东西。一个真正的 Xcode 工程,一个真正的 Visual Studio 工程。可以完整调用平台 API。UI 用系统自己的 WebView。各部分之间如何通信也完全自主。为了验证它是否真的可行,我们早期做了一个原型。能做半透明窗口吗?能在 WebView 内容之上叠原生 tooltip 吗?它会让你感觉是 Raycast 吗?原型出来,几乎和原生应用一模一样。半透明的 WebView 和窗口背景融为一体,tooltip、action panel 之类的浮层都是原生窗口。本质上还是我们花了多年打磨出的那套视觉语言。

不过,这不是银弹。这条路有实实在在的开销。在应用本身之上,我们等于还要自己搭起 Electron 开箱即用的那一整套基础设施。WebView、原生外壳、Node.js 后台之间的 IPC 要按平台逐个搭建、调试和优化。没有社区帮你解决这些问题。我们选它,是冲着 Raycast 自己的工作方式去的。对绝大多数桌面应用来说,这种取舍并不划算。Electron 应付得绰绰有余,还能帮你省下好几个月的基础设施工作。

我们也看过另外几个选项 —— Flutter、Qt、桌面版 React Native,以及在两个平台上都跑 Swift(向 The Browser Company 的勇气致敬,不过我们没那么爱冒险)。但都被很早就排除了。它们要么缺少我们需要的原生控制力,要么对我们的用户群还不够成熟,要么两者都有。

怎么搭起来的

从高层看,Raycast 2.0 由四部分组成:

  • 宿主应用(Host app): 每个平台都有自己的应用 —— macOS 用 Swift + AppKit 写,Windows 用 C# + .NET 8 + WPF 写。它负责所有必须由平台原生处理的事情:搭建窗口、监听全局快捷键、配置菜单栏或系统托盘等等。它还把 web 前端加载到平台的 WebView 里(macOS 上是 WKWebView,Windows 上是 WebView2),并监管 Node 后台。
  • Web 前端: 前端是一个 React + TypeScript 工程,同时面向两个平台。它包含所有 UI 代码,并按窗口(Launcher、AI Chat、Notes、Settings 等)分别打出独立的入口点。两个操作系统上跑的是同一份代码。
  • Node 后台: 一个长驻的 Node 进程承担应用的业务逻辑 —— 数据库访问、扩展运行时、其他长期服务等等。Node 是两个平台共同对话的中间层,意味着功能只要做一次。
  • Rust 内核: 性能或可移植性比开发便利更重要的地方,我们用 Rust。我们的数据层可以和 iOS 应用共享。云端同步层的 schema 和服务端共用。我们自研的文件索引器经过深度优化,能在几秒之内扫完整块硬盘。

多个运行时一起跑(Swift/C#、Node、WebView),各层之间需要互相对话。我们用混合方案 —— 平台原生的消息处理器加 stdio 通信 —— 把它们连起来。为了安全地处理这套通信,接口在一处统一声明,然后为每一端生成类型化的客户端。这样我们在四个运行时之间都拥有编译期保证。

Raycast v2 的技术栈

实际工作中,大部分团队成员都在 web 前端和 Node 后台里干活,功能就是从这里诞生的。需要从 OS 暴露新东西、或者为了原生手感做优化时(下面会讲),才会去碰原生外壳。一旦这四部分之间的边界定好,大部分产品工作就不需要跨越它们了。

全新的文件索引器

在 v1 里,文件搜索依赖 Spotlight 的元数据。它(大部分时候)能用,但我们只能用 Spotlight 已经索引到的内容,在 Windows 上更是完全没法用。在 v2 里,我们用 Rust 从零写了自己的文件索引器。它作为一个独立进程运行,直接扫描文件系统,构建搜索索引,然后通过文件系统事件保持更新。

在 Windows 上,用常规方式遍历 NTFS 太慢,达不到我们要的扫描速度。所以我们专门做了一个 NTFS 扫描器,直接读取主文件表(Master File Table,MFT)—— 这是把整块硬盘在秒级而不是分钟级索引完的唯一可行做法。

文件索引器是 Rust 性能价值最突出的地方之一。要在后台扫描几十万个文件、构建搜索索引,又不能影响应用其余部分。这件事能成立,靠的是 Rust 提供的可预测内存占用、以及没有 GC 停顿。

让它融入系统

当 UI 跑在 WebView 里,「感觉原生」到底意味着什么?对我们来说,可以归结为一个简单测试:如果有人在不知道 Raycast 底层用什么造的情况下使用它,他会不会觉得这就是一个普通的 Mac 应用?只要有任何一处感觉不对 —— 一个错位的动画、一个不该有的悬停态、一个被窗口边缘裁掉的浮层 —— 我们就还没做到位。

Raycast v2 的整体面貌

我们的一位 Windows 工程师说得很好:我们不是一个表层撒了点原生胶水的 web 应用 —— 我们是一个用 web 来做 UI 的原生应用。这个区分,决定了我们把时间花在哪里。下面要讲的大部分工作,不是关于「让它看起来对」,而是关于「让它表现得对」。

平台习惯

要让一个 web 应用在桌面上「不像桌面」,最容易的办法,就是在已经有原生约定的地方继续照搬 web 习惯。下面是我们刻意去匹配或回避的几件事:

  • 交互控件上不用 cursor: pointer。桌面应用不这么做。事情虽小,却会立刻让人意识到「这是个网站」。
  • 大多数控件上不要悬停高亮。在 macOS 上,按钮和列表项不会像 web 那样在鼠标悬停时高亮。
  • 设置在一个独立的原生窗口里打开,而不是模态框或侧边面板。
  • 浮层(popover)和 tooltip 渲染成原生窗口,而不是 WebView 内部的 DOM 元素。这样它们就能延伸到窗口边界之外,跟原生浮层一样。
  • 在 macOS Tahoe(macOS 新版本代号,Apple 在这一版本中引入了名为「Liquid Glass」的新视觉材质)上,我们采用了 Apple 新的 Liquid Glass 材质,好让 Raycast 在第一天就融入系统更新后的视觉语言。
  • 视图出现或切换时不能闪烁。这是 web 应用的常见破绽,我们花了大量功夫消除它。

这些是比较明显的部分。不那么明显的部分在下面。

顺着 WebKit,也绕开它

WebKit 是一个出色的渲染引擎,但它本来是为浏览网页而生,不是为一个每天显示和隐藏几百次的桌面应用而生。开箱状态下,它的很多假设对 Safari 完全合理,对我们却会出问题。我们花了不少时间,学着绕开这些假设。

  • 节流。 当 WebKit 认为某个视图不可见时,会对 requestAnimationFrame、CSS 动画和定时器进行节流。对于一个不停在显示和隐藏之间切换的启动器来说,这会把功能搞挂。我们的绕法是:把窗口排到最前但视觉上保持隐藏(alphaValue = 0),同时关掉 WebKit 的遮挡检测(windowOcclusionDetectionEnabled = false)。在窗口真正出现前,我们用一次 requestAnimationFrame 触发渲染,避免闪烁。
  • 被遮挡区域的渲染。 当 Raycast 从紧凑模式展开到完整大小时,WebKit 会让之前被遮住的区域留白一两帧 —— 它对那块「在视口之外」的区域做了节流。我们让 WKWebView 的 frame 永远保持展开后的尺寸,即使窗口本身仍是紧凑模式,问题就解决了。WebView 渲染了窗口可见边界之外的内容,所以当窗口展开时,内容已经在那里了。
  • 窗口缩放。 WebKit 会在窗口动画化缩放期间挂起绘制,造成肉眼可见的卡顿。我们覆写了 NSWindow.setFrame,把动画调用替换成隐式 Core Animation,让 WebView 在窗口缩放过程中也能持续渲染。
  • 打开窗口时的闪烁。 我们用 _doAfterNextPresentationUpdate(WebKit 用来把渲染状态和原生呈现同步的一个 API),保证 WebView 在窗口出现之前已经画完。没有它,你会看到一闪而过的过期或空白内容。
  • emoji 渲染。 最初我们的 emoji 选择器很慢,因为 WebKit 在为每一个 emoji 字形遍历整个字体回退链。修复办法其实很简单 —— 在启动时预热 emoji 字体 —— 但搞清楚问题到底出在哪,花了好一阵子。

我们还做了一套基础设施,可以在运行时切换 WebKit 的 Feature Flags(就是 Safari 开发菜单里那些)。我们内部用它来解锁 60 FPS 上限,并启用 requestIdleCallback 来调度非关键工作。

到了 Windows 这边

WebView2 是基于 Chromium 的,而 Chromium 对节流、渲染和进程管理有自己的一套想法。要让亚克力毛玻璃效果和自定义标题栏一起工作,需要原生外壳和 WebView2 运行时之间小心翼翼的协调。所有初始化参数都由我们自己控制,这样才能避开 WebView2 应用启动时常见的那种白屏一闪。

多窗口管理在 Windows 上也比 macOS 麻烦得多 —— 每个窗口都需要自己的 WebView2 环境,要正确配齐亚克力效果、自定义窗口外观和输入处理。我们还专门做了功课,确保 Chromium 在我们的窗口失焦时不会去节流 WebView,因为 Raycast 经常需要在其他应用后面也持续更新。

内存与性能

对 web 桌面应用最常见的批评是:慢、臃肿、吃内存。这是一个合理的关切,我们想坦诚地回应一下。

简短的答案:是的,Raycast v2 比 v1 用更多内存。这个增长是真实的,但同时也是有边界、可测量、并能持续改进的。整个团队把性能和内存当作头等优先级,而不是「以后再说」的事。

实际数字

Raycast v1(纯原生 UI,Node 后台只用于扩展)跑一段时间下来,通常稳定在 200–300 MB 上下。Raycast v2 在类似场景下稳定在 350–450 MB 上下。具体数字取决于你装了多少扩展、用了哪些功能、加载了多少内容。

数字是更高了,我们不打算回避这件事。这些数字也不是最终结果 —— 内存优化正是我们当下重点投入的方向,公测之后我们预期还能进一步压下来。下面是 v2 在主窗口隐藏时(Raycast 大部分时间都处于这个状态)的大致内存分布:

  • WebView(WebContent):~120–200 MB
  • Node.js 后台:~150–200 MB
  • 原生应用(Swift 外壳):~40 MB
  • WebKit GPU 进程:~18 MB
  • WebKit 网络进程:~12 MB

原生外壳很轻量。WebKit GPU 进程在窗口隐藏时会降到 20 MB 以下(在你主动使用 Raycast 时可能会冲高,但你关掉窗口后那部分内存就会释放)。两笔主要开销是 WebView 和 Node 后台。

作为参照,一个空 WebView 不加任何内容,基线开销大约是 50 MB;一个不导入任何模块的裸 Node.js 进程大约 12 MB。这些基线是这套方案自带的代价。剩下的部分是我们的应用代码、加载的模块、图标和缓存资源 —— 这些是我们可以控制、也在持续优化的东西。

内存不是「一个数字」

这并不让更高的内存占用变得无所谓,但能帮助你理解活动监视器(Activity Monitor)里看到的东西。当你在 Mac 上打开活动监视器,每个进程后面那个数字其实没看上去那么直接。macOS 会很积极地使用可用 RAM —— 它会缓存文件、压缩不活跃的页面、把东西留在内存里,好让系统更快。

有几件事值得知道:

  • 压缩内存。 当物理 RAM 紧张时,macOS 会压缩不活跃的页面,而不是把它们写到磁盘上。这个过程很快,意味着一个看上去用了 200 MB 的进程,实际成本可能小得多。一个空闲、堆很大的 Node 后台,压缩效果会很好。
  • 脏页 vs 净页。 不是所有驻留内存的代价都一样。净页(比如映射的二进制代码)可以被丢弃,需要时再从磁盘读回来,几乎不要钱。脏页(比如 V8 堆或解码后的图片)才是真正占成本的。我们的程序在磁盘上看起来很大,但其中大部分是净页内存,系统可以瞬间回收。
  • 共享框架。 活动监视器会把系统框架(WebKit、系统库)的内存,算到每一个使用它们的进程头上。当你把 Raycast 各进程的数字相加时,你其实在重复计算这些共享页。系统真实承担的成本,比活动监视器显示的要低。
  • 真正重要的是「内存压力(Memory Pressure)」。 活动监视器内存标签页底部那张图,才是判断你的 Mac 到底吃不吃力的真指标。如果它是绿的,说明系统还很宽裕,哪怕单个进程的数字看起来很高。这是系统在做它该做的事 —— 把可用 RAM 用起来让东西跑得更快,在别处有需要时随时归还。

这些都不是粗心对待内存的借口。我们盯着 phys_footprint(最接近活动监视器显示的那个数字),并主动在压低它。开发过程中我们已经把 v2 的占用显著砍下来了 —— 早期版本比现在高得多。我们也特意在内存更小的机器上测试,因为那才是这件事真正重要的地方。但我们希望读者在看那些数字时,有一个正确的心智模型。

抛开内存不谈,有一些地方 v2 明显比 v1 快。

  • 搜索。 v2 的根搜索包含了完整的文件搜索,由我们新写的 Rust 文件索引驱动。在 v1 里,文件搜索只有单独的命令能用,而且依赖 Spotlight 的元数据。新索引器直接搜你的文件,不依赖 Spotlight,同时让整个搜索体验保持流畅。
  • 文本渲染。 AI Chat 以及任何涉及富文本渲染的功能,是 WebKit 真正发光的地方。web 上几十年的文字布局和渲染优化,在这里都体现出来:滚动长对话、渲染 markdown、处理带语法高亮的代码块。macOS 上的 TextKit 也能干这活,但 WebKit 在这种工作负载上投入要多得多。

我们还没完成。内存和性能是我们当下重点投入的方向,我们知道还有可压缩空间。团队正在进一步降低稳态占用,让更多前后端代码懒加载,优化图标和图片处理,把 V8 堆收紧。毕竟它还在公测中。

取舍

没有重写是免费的。下面是什么变好了、什么变难了。

变好的部分

先说积极的一面。我们觉得 Raycast 第二版以下这些方面有了实打实的提升:

  • 开发速度。 这是最大的一项。热重载让 UI 改动在不到一秒就能看见,而 v1 要重新编译 Swift target 再重启应用。我们能更快地做原型、迭代、改 bug。这件事直接惠及用户 —— 功能上线更早,修复落地更快。
  • 一个团队,两个平台。 大部分产品工作发生在共享的 web 前端和 Node 后台。当我们发布一个功能时,它在 macOS 和 Windows 上同时可用。在 v1 里,每一处 UI 改动按定义就是 macOS 专属。另外,移动端团队也会受益于 Rust 数据层和新的同步引擎。
  • 招聘。 找一个能写 React、TypeScript、Node 的工程师,比找一个 AppKit 深度选手容易得多。这不意味着我们不再需要原生工程师 —— 我们仍然有专门的 Swift 和 C# 工程师在做宿主应用 —— 但大部分产品工作不再要求特定平台的深度知识了。
  • 更丰富的 UI。 有些事就是用 web 栈做起来更好:富文本编辑、markdown 渲染、带动画的复杂布局。Notes 和 AI Chat 都从中受益。它也给了我们成熟的积木去做编辑、解析、渲染,同时仍然让我们掌握那些让 Raycast 之所以是 Raycast 的部分。
  • 扩展更简单了。 既然 Node.js 现在和应用一起内置,第一次从扩展商店装扩展时,你就不用单独下载 Node 了。而且因为应用本身和扩展跑在同一套栈上(React、TypeScript、Node),做内部功能和做扩展几乎是同一种体验。

变难的部分

不是一切都完美,所以这是这套更复杂的技术栈带来的代价:

  • 更高的内存基线。 上面那一节讲过了,v2 用的内存比 v1 多。WebView 和 Node 进程加了一笔纯原生应用没有的基线。在 web 栈上把内存压低是可以做到的,只是要更刻意地去花功夫。我们在主动缩小差距,预期公测之后这些数字会继续下降。
  • 栈复杂度。 四种运行时(Swift 或 C#、Node、WebView、Rust)意味着活动的部件更多。排查一个问题可能让你从 React 前端经过 IPC 走到 Node 后台,再扎进一个 Rust 模块。类型化的 IPC 代码生成(codegen)帮助各端保持一致,但客观上,这套栈就是比单语言的原生应用更复杂。
  • Windows 的多样性。 比起 macOS,Windows 是一个更多样的平台。用户跑着不同的操作系统版本、硬件配置和显示设置 —— 8 GB 内存配上 4K 显示器和一颗略旧的 CPU 一点不稀奇。使用系统自带的 WebView 也意味着不同机器上 WebView2 的版本可能不同,所以我们需要考虑不一样的渲染行为和 API 可用性。测试面积更大,要处理的边界情况也更多。
  • 某些原生小细节变难做。 那些在 AppKit 里免费就能拿到的东西 —— 某些辅助功能行为、拖放的边界情况、IME 处理 —— 在 WebView 里都要显式去做。重要的那些我们已经处理掉了,但还有一长尾的小行为需要照顾,我们仍在一项项推进。
  • 窗口按需启动。 在 v1 里,像 AI Chat 和 Notes 这种窗口一旦被调起就一直留在内存里,所以下次按下热键它会立刻出现。在 v2 里,我们更激进地拆掉不活跃的窗口以控制内存,这意味着冷启动时会有短暂的延迟。我们在调这个平衡 —— 在你快速切换窗口时加上一段宽限期让它们保持「热」,同时在不再需要时回收内存。

我们觉得这些取舍是值得的。不是因为坏的那部分不重要,而是因为开发速度、跨平台触达、招聘上的收益,长期会直接转化成更好的产品。难的部分,靠工程力气能解决。好的那部分,换别的路几乎拿不到。

接下来要去哪

如果你一路读到这里,可能在等一个「哪种方案最好」的定论。我们其实不那么想问题。我们把代码当作手段,而不是目的。重要的是产品,不是技术栈。我们是自己的用户,每天在自己拥有的每台机器上用 Raycast,如果它感觉不对,我们就不会发出去。这就是我们的底线,也是这次重写花了这么久的原因。

Raycast 2.0 现在已经进入公开公测。如果有任何地方感觉不对、感觉慢、或者不像 Raycast,告诉我们。这正是我们现在最需要的反馈。

也要快速地感谢一下把这件事推到现在的团队。从一个原型出发,如今它已经送到所有想试试它的人手上。这背后离不开海量的努力和对细节的死磕。

我们做这件事,是为了继续推动「桌面上的生产力」这件事的边界 —— 尤其是在 AI 正在改变人们和机器交互方式的当下。带着这套新代码,我们可以走得更快,在两个平台上交付高品质的应用,继续贴近用户真正需要的东西。后面还有很多东西在路上。回头见!

相关笔记