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

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 26+
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

Building for the desktop only? GPUI Kit, from the same Longbridge team, is a ready-to-use UI component library for macOS, Windows and Linux. Much of NSH's design comes from GPUI Kit, and apps built with NSH embed directly in GPUI Kit apps.

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

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