このページは機械翻訳のため、誤りを含む場合があります。 英語の原文を読む

Native Shell

一度作れば、どこでもネイティブ

アプリは中央にあります。その周りのアーチはそれぞれプラットフォームです。

TSX を書くだけで、Rust に並ぶ速度のネイティブクライアントを出荷できます。1 つのコードベースが 8 つのプラットフォームで動作し、それぞれのシステム固有の UI とインタラクションを備えます。

FPS
120
初回フレーム
~1 ms
プラットフォーム
8

Rust のすぐ隣で動く、TSX で書かれたクライアント

NSH で JS なのはアプリのコードだけです。リアクティブグラフ、レンダラー、レイアウト、そしてすべてのモジュールは Rust です。TSX アプリと Rust アプリは同じネイティブ実行パイプラインを共有します。

120 FPS
22 本のライブスパークライン、板情報、歩み値が同時に更新され、フレーム p95 は 4 ms 未満
~0.1 ms
エンジンの起動 (75–155 µs)
~1 ms
カウンターアプリの初回フレーム
~0.12 ms
1 回のタップ、イベントからコミットまで
< 1 KB
hello world のアプリコード
~1.1 MB
Web での TSX アプリのダウンロードサイズ (Wasm ランタイム)
小さなアプリでの計測結果:素のシステムウィンドウより数 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 のダウンロード——

いずれもリリースビルドです。静止時のメモリ:macOS のフットプリントは Apple M3 で計測 (カウンターアプリと Rust テンプレートは 1200 × 800、システムウィンドウはラベル 1 つで 480 × 640)、Linux のプライベートダーティメモリは NVIDIA RTX 3060 Ti で計測 (nsh init の TSX テンプレートと Rust テンプレート、およびラベル 1 つの GTK ウィンドウで、いずれも 480 × 640)、Windows はプライベートワーキングセットです。各プラットフォームの計測方法と RSS、USS はレポートに掲載しています。サイズ:nsh package のリリースビルドで、リンク時最適化とストリップを適用しています。TSX 実行ファイルには QuickJS ランタイムが含まれ、アプリのコードは 1 KB 未満です。パッケージは macOS では zip 圧縮した .app、Linux では .tar.gz、Windows では .zip で、Web ではブラウザがダウンロードするもの (ランタイムとアプリ) です。 パフォーマンスレポート全文

依存関係 0 の TSX

アプリは以下のファイルだけで構成されます。nsh コマンドラインは独自のバンドラーとツールチェーンを内蔵しており、import するモジュールはネイティブです。

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

my-app/
  App.tsx
  nsh.json
  tsconfig.json
  • 1 つの 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 を構築します。テキスト入力、選択、入力メソッド、アクセシビリティはシステムが提供するため、新しい OS リリースで改善された点もそのままアプリに反映されます。

プラットフォームビューテキストテキスト入力要件
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 アプリに組み込み

マップ、Web ビュー、動画、そしてホストが登録した任意のネイティブビューは、同じ階層内の 1 つのサブビューとして扱われ、アプリの他の部分と一緒にレイアウト、クリップ、スクロール、タッチ操作を受け付けます。 各プラットフォームのバックエンド

1 つのアプリを、各デバイス固有のかたちで

同じコンパイル済みコードがここで 2 回動いており、違うのはデバイスだけです。ポインターがある環境では、会話一覧を脇に置いたウィンドウになります。スマートフォンでは、ボタンとコンポーザーが 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 の Web ランタイム上でブラウザ内でライブ動作しています。ターミナルは nsh run --target tui の実際の出力です。

必要なときは、同じエンジンを Rust で

Rust はアプリ全体を書くためのファーストクラスの言語です。UI、ロジック、コンポーネントがネイティブコードにコンパイルされ、要求の厳しいビジネスロジックにも対応できます。どちらも同じ element と signal を使い、TSX の画面から Rust のコンポーネントを import できます。

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())),
        )
}

変更されたビューだけを更新します

すべての element は、プラットフォームが保持するビューです。変更があると、変更された signal を読む式だけが、フレームごとに 1 回のバッチで実行されます。メインスレッドが JS を待つことはなく、ネイティブアニメーションも JS なしで動くため、スクロールとアニメーションは 120 FPS を維持し、静止時のアプリはほぼアイドル状態です。

高速さを保つしくみ

signal を読む 2 つのテキストが変化し、履歴に 1 行が追加されます。他のビューには一切手が加わりません。

$ 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 を追加できます

チームが書くことも、リクエストごとにモデルが生成することもできます。UI は署名付きモジュールとしてデバイスに届き、ホストが設定したサンドボックス内で動作し、ネイティブに表示されます。

  1. 書く

    チームまたはモデルが TSX モジュールを書きます。

    App.tsx
  2. 検査する

    型チェックを行い、ヘッドレスでテストを実行します。

    nsh check nsh test
  3. 署名する

    Ed25519 で署名された .nshpkg。リリース鍵はオフラインのルートによって証明されます。

    nsh publish
  4. 信頼境界

    実行する

    デバイスが署名、証明書、失効情報を検証します。各ビューには、メモリ、呼び出し時間、ノード数の上限が設定された VM が割り当てられ、ホストが許可した capability だけが使えます。

    NshAppView NshView AppView

3 つの出荷方法、1 つのエンジン

スタンドアロンアプリ

Rust、TSX、またはその両方で書き、インストール可能な iOS、Android、macOS、Windows、Linux アプリ、または静的な Web サイトとしてパッケージ化します。

$ 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 には、状態、フォーカス、キーボード、アクセシビリティを担うスタイルなしのベースコンポーネントが揃っており、各パーツのスタイルはアプリ側で指定します。それ以外は 1 つの nsh コマンドラインで完結します。

スタイルなしのベースコンポーネント

ナビゲーション、ダイアログ、メニュー、ポップオーバー、タブ、セレクト、データテーブル、コードエディタ、リッチテキストカーネルを用意しています。各パーツは Tailwind クラスまたは型付きスタイルでスタイリングできます。

nsh/base

人にもエージェントにも使える 1 つのツールチェーン

1 つの nsh 実行ファイルで、作成、ビルド、型チェック、ホットリロード、テスト、パッケージ化、署名まで行えます。型宣言は Rust のソースから生成され、ドキュメントは llms.txt として提供されるため、エージェントも同じコマンドで一連のループをすべて実行できます。

nsh run, nsh test, nsh package

すべてのターゲットで検証

同じテストを、ヘッドレス環境、実際のウィンドウ、iOS シミュレーター、Android デバイス、ブラウザで、各プラットフォーム固有の入力を使って実行します。

nsh test --target ios

ネイティブなビューとインタラクション、
あなた自身のビジュアルデザイン

nsh をインストールすれば、1 分で最初の TSX アプリが動きます。