平台的基础设施
每个后端都用平台已有的东西构建界面:UIKit 视图、Android 视图、AppKit 视图、GTK 4 控件、用 Direct2D 和 DirectWrite 绘制的 Win32 窗口,以及用 CSS 设置样式的 HTML 元素。文本输入、选区、输入法和无障碍都来自平台。
NSH 是一个 Rust First 的全平台 Native UI 框架:一套应用模型、一个工具链,构建 iOS、Android、macOS、Windows、Linux 和 Web 应用,每个平台都建立在系统自己的界面与交互之上;同一个引擎还能在发布之后下发团队编写或 AI 生成的新界面。
nsh init my-appNSH 统一组件、状态、业务逻辑和 API;视觉由应用设计,视图与交互建立在平台自己的基础之上。
每个后端都用平台已有的东西构建界面:UIKit 视图、Android 视图、AppKit 视图、GTK 4 控件、用 Direct2D 和 DirectWrite 绘制的 Win32 窗口,以及用 CSS 设置样式的 HTML 元素。文本输入、选区、输入法和无障碍都来自平台。
同一个组件在每种设备上都按用户的习惯工作。Menu 在有指针的设备上是贴着触发点弹出的菜单,在手机上是从底部升起的面板;ContextMenu 用右键打开,在触屏上长按打开。应用只写一次菜单。
--target ios--target android--target macos--target windows--target linux--target web--target gpui跨平台在这里的意思是:每种设备都得到它该有的应用。布局随窗口重组,输入方式随设备变化,业务逻辑保持一份。一个工程师可以写一次应用,并在每个目标上验证它。
Flexbox 和 Grid 由 Taffy 排版,配合 Tailwind 断点(md:、lg:)和 Rust 里的 window() 尺寸:聊天应用在宽窗口里并排显示侧栏、会话列表和消息,在手机上一次显示一屏。
桌面上是指针、悬停、右键、快捷键和焦点;手机上是触摸、手势、软键盘和页面导航。nsh/base 的基础组件同时承载两者,应用代码只有一份。
nsh test 无界面地运行应用的测试,也能在 macOS、Windows、Linux 和 GPUI 窗口、iOS 模拟器、Android 设备或浏览器里,用平台自己的输入运行同样的测试。
Rust 是编写完整 NSH 应用的一等语言:界面、逻辑和组件都编译成 Native 代码,带着 Rust 的所有权、类型、线程和 crates.io。TSX 是第二条路径,适合快速写界面、做插件和运行时下发的界面。两者运行在同一个引擎上,使用同样的基础元素、基础组件和 signal,TSX 页面还能直接导入你的 Rust 组件。
用 Rust 开发use nsh_core::element::prelude::*;
#[component]
pub fn App() -> impl IntoElement {
let count = signal(0);
view()
.items_center()
.gap_4()
.child(text(move || format!("Count: {}", count.get())))
.child(
view()
.rounded_lg().bg(rgb(0x2563eb)).px_5().py_3()
.on_press(move || count.update(|n| *n += 1))
.child(text("Tap").text_color(white())),
)
}import { mount, signal, Text, View } from "nsh";
function App() {
const count = signal(0);
return (
<View class="items-center gap-4">
<Text>Count: {count()}</Text>
<View
class="rounded-lg bg-blue-600 px-5 py-3"
onPress={() => count.update((n) => n + 1)}
>
<Text class="text-white">Tap</Text>
</View>
</View>
);
}
mount(App);文本、输入、Native 组件和 Web 是 UI 框架里最难做好的部分,NSH 直接交给平台;它自己的力气花在引擎、基础组件和工具链上。
文本框就是平台的编辑器——UITextView、EditText、NSTextView、GTK 的 TextView、浏览器的输入框——带着系统的输入法、选区手柄、编辑菜单和快捷键;Windows 上是 NSH 基于 DirectWrite 和 IMM32 的编辑器。系统在新版本里对文本和输入的改进,应用直接获得。
地图、网页视图、视频播放器和宿主注册的任何 Native 视图,都只是同一层级里的又一个子视图:它们与 NSH 的视图一起布局、裁剪、滚动和接收触摸,没有分层问题。
在 Web 上,NSH 渲染用 CSS 设置样式的 HTML 元素;浏览器负责滚动、选择文本和处理输入。nsh package --target web 发布一个按内容命名文件的静态网站,可以直接放进任何 CDN 缓存。
每个元素都是平台保留着的视图。一次变化只重新运行读到那个 signal 的表达式,只发送它改到的视图,每帧一次批量提交;没变的视图原样不动,Native 动画不经过 JS 逐帧计算。聊天、表单、列表这类日常界面因此在静止时几乎不占 CPU,在 ProMotion 屏幕上滚动和动画稳定在 120 FPS。
nsh/base 提供无样式的基础组件——导航、对话框、菜单、弹出层、标签页、复选框、面板——负责状态、焦点、键盘和无障碍;每个部分的样式由应用决定,用 Tailwind 类或类型化样式。
一个 nsh 可执行文件负责创建、构建、类型检查、热更新开发、测试、打包和签名,覆盖每个目标平台,并自带 TSX 打包器和 TypeScript 检查器。每个 API 都有从 Rust 源码生成的类型声明,文档以 llms.txt 提供,AI agent 用同一套命令就能完成生成、运行、测试、修复和打包的整个循环。
独立应用是 NSH 的起点。在同一个引擎之上,应用还可以在运行时接收新界面——由团队编写,或由模型生成——以签名模块的形式,运行在宿主设定的沙箱里。
用 Rust、TSX 或两者编写应用,nsh package 把它打成可安装的 iOS、Android、macOS、Windows、Linux 应用,或一个静态网站。
$ nsh init my-app --template rust
$ nsh run my-app --target macos
$ nsh package my-app --target windows
# --template ts for TSX
# ios · android · macos · windows · linux · gpui · web在任意页面嵌入 NshAppView(iOS)、NshView(Android)、AppView(桌面与 GPUI 应用)或在网页里用 createPlayer,运行时加载签名的 TSX 模块。
let app = NshAppView()
try app.grantCapabilities(grants)
let info = try await app
.loadPackage(at: file, trust: trust)
// verified, sandboxed, running
// NshView on Android, AppView elsewhere模型为每次请求写一个模块,服务器检查并签名,应用在沙箱里以 Native 方式显示它。
import { mount, signal, View } from "nsh";
function Answer() {
const range = signal("1W");
return <View class="gap-3 p-4">…</View>;
}
mount(Answer);模块以 Ed25519 签名的 .nshpkg 到达,发布密钥由离线根密钥认证,设备端检查吊销列表;每个视图一个 QuickJS 虚拟机,内存、调用时长和节点数都有上限,只能使用宿主授予的能力。
同一个应用在每个平台上功能一致:一套组件、状态和逻辑。它在手指或指针下的手感、文本如何编辑、菜单如何打开——这些属于每个平台自己。
开始构建