本頁由機器翻譯,可能有錯誤。 閱讀英文原文

Native Shell

一次構建,原生運行於各平台

你的應用程式位於中央,周圍每道弧形代表一個平台。

只需編寫 TSX,即可發佈與 Rust 同步的原生客戶端。一套程式碼可在 8 個平台上運行,各自使用系統原生的 UI 與互動方式。

FPS
120
首幀
~1 ms
平台
8

以 TSX 編寫的客戶端,效能緊貼 Rust

在 NSH 中,只有你應用程式的程式碼是 JS。響應式圖、渲染器、佈局及每個模組都是 Rust。TSX 應用程式與 Rust 應用程式共用同一條原生執行管線。

120 FPS
22 條即時迷你走勢圖、一個訂單簿及一個成交記錄同步更新,幀 p95 低於 4 ms
~0.1 ms
引擎啟動(75–155 µs)
~1 ms
計數器應用程式的首幀
~0.12 ms
一次點按,由事件到提交
< 1 KB
Hello world 的應用程式碼
~1.1 MB
TSX 應用程式在網頁上的下載量(Wasm runtime)
小型應用程式實測:僅比系統空白視窗多出數 MB
TSXRust系統空白視窗
記憶體佔用,macOS~20 MB~19 MB~17 MB
私有髒記憶體,Linux(GTK、GL)~48 MB~44 MB~38 MB
比系統視窗多出的部分,macOS / Linux+3 / +9 MB+2 / +5 MB
私有工作集,Windows~15 MB~14 MB
各平台的大小:nsh init 的 hello world
TSX 可執行檔TSX 套件Rust 可執行檔Rust 套件
macOS (Apple silicon)19.8 MB9.5 MB12.4 MB6.0 MB
Linux (x86_64)25.7 MB11.6 MB16.5 MB7.5 MB
Windows (x86_64)28.6 MB12.4 MB15.0 MB6.7 MB
Web—下載約 1.1 MB——

均為 release 構建。靜止時的記憶體:macOS 佔用量於 Apple M3 上量度(計數器應用程式及 Rust 範本為 1200 × 800;系統視窗放置一個標籤,為 480 × 640);Linux 私有髒記憶體於 NVIDIA RTX 3060 Ti 上量度(nsh init 的 TSX 與 Rust 範本,以及放置一個標籤的 GTK 視窗,均為 480 × 640);Windows 為私有工作集。各平台的量度方法,連同 RSS 與 USS,均載於報告中。大小:取自 nsh package 的 release 構建,已啟用連結時最佳化並移除符號;TSX 可執行檔內含 QuickJS runtime,應用程式碼不足 1 KB;套件在 macOS 上是壓縮後的 .app,在 Linux 上是 .tar.gz,在 Windows 上是 .zip;網頁則為瀏覽器所下載的內容,包括 runtime 與應用程式。 完整效能報告

TSX,零依賴

一個應用程式就只有下面這些檔案。nsh 命令列自帶打包器與工具鏈,而你匯入的模組都是原生的。

$ nsh init my-app --template ts
$ nsh run my-app --target macos

my-app/
  App.tsx
  nsh.json
  tsconfig.json
  • 一個 nsh 可執行檔即可建立、運行、測試及打包
  • 由儲存檔案到新版本運行,約 120 ms
  • nsh check 使用 TypeScript 7 的原生 tsc 進行型別檢查
TSXApp.tsx
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);

8 個平台,各用系統原生控件

NSH 以各平台現有的元件構建每個 UI。文字輸入、選取、輸入法及無障礙功能均來自系統,新版作業系統為這些功能帶來的改進,也會隨之惠及你的應用程式。

平台視圖文字文字輸入需求
macOSAppKitCoreTextNSTextViewmacOS 14+
LinuxGTK 4PangoGtkTextViewGTK 4.14+
WindowsWin32, Direct2DDirectWriteNSH 的編輯器、IMM32Windows 10 1809+
WebHTML, CSS瀏覽器自身的文字<input>, <textarea>現代瀏覽器
iOSUIKitCoreTextUITextViewiOS 15+
AndroidAndroid 視圖StaticLayoutEditTextAPI 28+
TUIratatui終端機的字體Readline 按鍵、輸入法macOS, Windows, Linux
GPUIGPUIGPUI 的文字系統NSH 的編輯器內嵌於 GPUI Kit 應用程式

地圖、網頁視圖、影片,以及宿主註冊的任何原生視圖,都只是同一視圖層級中的另一個子視圖:它們與應用程式其餘部分一同佈局、裁剪、捲動及接收觸控。 各平台的後端

一個應用程式,適應每種裝置的形態

同一份編譯後的程式碼在此運行兩次,只有裝置不同。使用指標裝置時,它是一個視窗,對話列表在側邊;在手機上,按鈕與輸入框以 Liquid Glass 效果浮於其上,對話串在下方捲動;在終端機中,它則位於字元網格上。應用程式只需編寫一次。

桌面即時體驗:輸入一則訊息
手機即時體驗:輸入一則訊息
終端機探索 TUI
+ New chat           │ What makes an NSH app native?
Today                │ A signal holds the count; only the Text that reads it updates   │
What makes an NSH…   │ when you press.                                                 ┃
Plot a week of NSH   │ import { mount, signal, Text, View } from "nsh";                ┃
Signed packages,…    │                                                                 ┃
A sheet with two…    │ const count = signal(0);                                        ┃
Taffy grid vs flex   │ mount(() => (                                                   ┃
                     │   <View onPress={() => count.set(count() + 1)}>                 ┃
                     │     <Text>{count()}</Text>                                      ┃
                     │   </View>                                                       ┃
                     │ ));                                                             ┃
                     │                                   What makes an NSH app native? ┃
                     │ Every element is the platform's own view: UIKit on iOS, Android ┃
                     │ views, AppKit on macOS and GTK 4 on Linux. Your TSX describes   ┃
                     │ them; layout, text and the reactive graph run in Rust.          ┃
                     │                                 And the glass around this chat? ┃
                     │ It's Liquid Glass, one glassEffect style. iOS 26 and macOS 26   ┃
                     │ draw the system's material; elsewhere it blurs what scrolls     ┃
                     │ beneath.                                                        ┃
                     │ • The buttons and the composer float over the thread            ┃
                     │ • Messages pass under them as you scroll                        ┃
                     │ • The same code at every idiom                                  ┃
                     │ ╭───────────────────────────────────────────────────────────────╮
                     │ │ › Ask anything                                                │
                     │ ╰───────────────────────────────────────────────────────────────╯

桌面與手機版本在你的瀏覽器中,透過 Native Shell 的網頁 runtime 即時運行。終端機版本是 nsh run --target tui 的真實輸出。

需要時,同一引擎也可用 Rust

Rust 是編寫完整應用程式的一等語言:UI、邏輯及元件均編譯為原生程式碼,足以應付要求嚴苛的業務邏輯。兩者使用相同的元素與 signal,而 TSX 畫面可匯入你的 Rust 元件。

使用 Rust 構建

Rustsrc/lib.rs
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())),
        )
}

只更新有變動的視圖

每個元素都是平台所保留的一個視圖。當 signal 變動,只會執行讀取該 signal 的表達式,並於每幀以單一批次處理。主執行緒從不等待 JS,原生動畫亦無須 JS 即可運行,因此捲動與動畫能維持 120 FPS,應用程式靜止時幾近閒置。

如何保持高速

讀取這些 signal 的兩段文字會更新,歷史記錄新增一行;其他視圖完全不受影響。

$ nsh test examples/counter/App.tsx --trace
perf commit: 56 ops, 50 mount items, 21 nodes
press(+1)
perf commit: 10 ops, 7 mount items, 24 nodes

發佈之後,你的應用程式仍可加入新的 UI

可由你的團隊編寫,也可由模型按每次請求生成。它以已簽署模組的形式送達裝置,在宿主設定的沙盒中運行,並以原生方式顯示。

  1. 編寫

    由你的團隊或模型編寫 TSX 模組。

    App.tsx
  2. 檢查

    先進行型別檢查,再以無頭模式運行其測試。

    nsh check nsh test
  3. 簽署

    以 Ed25519 簽署的 .nshpkg,其發佈金鑰由離線根金鑰認證。

    nsh publish
  4. 信任邊界

    運行

    裝置會驗證簽章、證書及撤銷記錄。每個視圖獲分配一個 VM,設有記憶體、呼叫時間及節點上限,並只擁有宿主授予的能力。

    NshAppView NshView AppView

三種發佈方式,同一個引擎

獨立應用程式

以 Rust、TSX 或兩者混合編寫,並打包成可安裝的 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
開始使用

動態 UI 與外掛

在任何畫面嵌入一個視圖,並在運行時載入已簽署的 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
各平台的嵌入方式

AI 生成的 UI

模型按每次請求編寫一個模組,讓應用程式以介面作出回應,並由你的伺服器檢查及簽署。

import { mount, signal, View } from "nsh";

function Answer() {
  const range = signal("1W");
  return <View class="gap-3 p-4">…</View>;
}
mount(Answer);
簽署與沙盒

行為由 NSH 提供,外觀由你決定

nsh/base 提供無樣式的基礎元件,負責狀態、焦點、鍵盤操作及無障礙功能,而應用程式可為每個部分設定樣式。其餘工作都由一個 nsh 命令列完成。

無樣式的基礎元件

導覽、對話框、選單、彈出層、分頁、下拉選擇、資料表格、程式碼編輯器及富文字核心。可使用 Tailwind class 或具型別的樣式,為每個部分設定外觀。

nsh/base

一套工具鏈,同時適用於人與 agent

一個 nsh 可執行檔即可建立、構建、型別檢查、熱重載、測試、打包及簽署。型別宣告由 Rust 原始碼生成,文件則以 llms.txt 提供,讓 agent 以相同的命令完成整個流程。

nsh run, nsh test, nsh package

每個目標平台都經過驗證

相同的測試可以無頭模式運行,亦可在真實視窗、iOS 模擬器、Android 裝置或瀏覽器中,以該平台自身的輸入方式運行。

nsh test --target ios

原生的視圖與互動,
由你設計外觀

安裝 nsh,一分鐘內運行你的第一個 TSX 應用程式。