约 9 分钟
示例
todo、chat、timeline、mail、disktree 等示例应用,以及可直接下载运行的独立构建。
仓库内的示例
| 示例 | 运行 | 展示内容 |
|---|---|---|
| todo | 在 example-app/ 中执行 bun run dev |
起点:单个文件、一个 <virtual-list>、一个原生 <input>,以及一个带动画的侧边栏 |
| blurred window | bun run blurred-window |
macOS 磨砂玻璃表面,使用 GPUI 原生的 vibrancy 背景与透明标题栏 |
| chat | bun --hot chat.tsx |
一个 GPUIX 应用:透明标题栏、带动画的侧边栏、按线程的对话记录、示例回复、输入框,以及 <markdown> |
| timeline | bun --hot timeline.tsx |
视频编辑器时间轴:片段拖拽、带吸附的边缘裁剪、播放头拖动、框选、指针下缩放,以及带冻结标尺与轨道列的双轴平移 |
bun --hot mail.tsx |
类 Superhuman 的邮件客户端:三栏布局、线程列表,以及一封 Framer 新闻信 | |
| disktree | npx disktree [dir],源码位于 disktree/ |
tobi/disktree 的移植版,已发布到 npm:在 worker 线程上扫描文件夹或整块磁盘,在磨砂窗口上绘制半透明矩形树图,并对可回收空间排序 |
| native-text | bun --hot native-text.tsx |
三个原生文本组件,带切换标签页 |
| counter | bun --hot counter.tsx |
尽可能小的应用:状态、事件、hover |
| diff | bun --hot diff.tsx |
一个用 JS 中的 <div> 和 <text> 组合而成的 diff 查看器,用于对比 |
| web | 从仓库根目录执行 bun run web |
在浏览器 canvas 中用 WebGPU 渲染的 ChatGPT 示例 |
待办应用位于 example-app/,可以通过 bunx @gpuix/cli new 复制。其余示例位于 examples/。
下载独立构建
或者从 GitHub release 下载一个独立的 chat 构建。无需安装 Bun 或 Rust。
tar -xzf example-chat-aarch64-apple-darwin.tar.gz
./example-chat-aarch64-apple-darwin
压缩包会保留可执行位,因此无需 chmod 步骤。macOS 在首次运行时仍可能拦截未签名的二进制文件。请右键点击该文件,选择 打开 并确认。
在 Windows 上,下载 example-chat-x86_64-pc-windows-msvc.exe 并双击运行。在 Linux 上,文件是 example-chat-x86_64-unknown-linux-gnu.tar.gz。
浏览器中的 Web 示例
Web 示例打包了与桌面 chat 示例相同的 React 应用与协调器(reconciler)。wasm-bindgen 会把 mutation 与事件回调暴露给既有的 retained tree 和 GpuixView,它们运行在 GPUI 的浏览器平台之上。
Web 构建需要 nightly 版 Rust 以及与之匹配的 wasm-bindgen CLI:
rustup toolchain install nightly --component rust-src --target wasm32-unknown-unknown
cargo install wasm-bindgen-cli --version 0.2.127 --locked
bun run web
生成的 Wasm 使用了共享内存,因此页面必须是跨源隔离(cross-origin isolated)的。生产服务器必须在顶层文档上发送以下响应头:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
require-corp 进而会约束跨源子资源,这些子资源必须自己提供 CORS 或 Cross-Origin-Resource-Policy。只要把 JavaScript 与 Wasm 和文档放在同一源下,就无需其他操作。
bun run web 只有在 packages/native/wasm 缺失时才会重新构建 Wasm。在修改了 Rust 代码后,可以强制重建:
bun scripts/web.ts --rebuild
浏览器中的热重载
bun run web 通过 Bun 的前端开发服务器来提供示例,因此对 examples/chat.tsx 的修改会以一次 React Fast Refresh 更新的形式到达。组件会就地替换,且 useState 得以保留,这意味着输入框文本、侧边栏选中项以及滚动位置都会保持在原处。GPUI 的 canvas 绝不会被重建,约 19 MB 的 Wasm 模块也绝不会被重新拉取。
Fast Refresh 只适用于「所有导出都是组件」的模块。如果修改的是其他内容(例如入口文件),Bun 会改为重载页面。两条路径都正确,只是重载会慢一些。
Wasm 这半部分是单例,绝不能重复求值。WebGpuixRenderer::init 在其 thread-local 应用已经存在时会以 GPUIX web is already running 报错,而 GPUI 的浏览器平台会把自己的 canvas 追加到 <body>。保护它的关键并不在于它身处 node_modules 中 —— Bun 会把它和你的应用打包进同一个客户端注册表。关键在于 Bun 只会重新运行改动过的模块,然后沿其导入者向上回溯,因此未改动的依赖会保持已求值且被缓存。由此得出两条规则:
- 不要在入口文件中调用
import.meta.hot.accept("./your-app", ...)。Bun 哪怕在被导入模块已经自行接受(self-accepted)时,也会运行导入者的依赖接受回调,于是该回调会在一次成功的刷新之上再次挂载组件树,并丢弃所有useState。 - 把
@gpuix/native的导入放在一个「永远不会成为 Refresh 边界、也绝不被显式接受」的模块里。