Эта страница переведена машинным способом и может содержать ошибки. Читать оригинал на английском

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)
Измерения небольших приложений: на несколько MB больше, чем голое системное окно
TSXRustГолое системное окно
Memory footprint, macOS~20 MB~19 MB~17 MB
Private dirty memory, Linux (GTK, GL)~48 MB~44 MB~38 MB
Сверх системного окна, macOS / Linux+3 / +9 MB+2 / +5 MB
Private working set, Windows~15 MB~14 MB
Размер на каждой платформе: hello world из nsh init
Исполняемый файл 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——

Релизные сборки. Память в покое: footprint macOS на Apple M3 (приложение-счётчик и шаблон Rust при 1200 × 800; системное окно содержит одну надпись при 480 × 640); private dirty memory Linux на NVIDIA RTX 3060 Ti (шаблоны TSX и Rust из nsh init и окно GTK с одной надписью, всё при 480 × 640); private working set Windows. Способ измерения для каждой платформы, включая RSS и USS, приведён в отчёте. Размеры: релизные сборки nsh package, с оптимизацией на этапе компоновки (link-time optimization) и без символов; исполняемый файл TSX содержит рантайм QuickJS, код приложения занимает менее 1 KB; пакет — это упакованный в zip .app на macOS, .tar.gz на Linux и .zip на Windows; для веба — то, что скачивает браузер: рантайм и приложение. Полный отчёт о производительности

TSX с 0 зависимостей

Приложение — это файлы ниже. Командная строка 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 проверяет типы нативным tsc из TypeScript 7
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, Direct2DDirectWriteРедактор NSH, IMM32Windows 10 1809+
WebHTML, CSSБраузерный<input>, <textarea>Современные браузеры
iOSUIKitCoreTextUITextViewiOS 15+
AndroidПредставления AndroidStaticLayoutEditTextAPI 28+
TUIratatuiШрифт терминалаКлавиши readline, методы вводаmacOS, Windows, Linux
GPUIGPUIТекстовая система GPUIРедактор 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. Терминал — реальный вывод nsh run --target tui.

Тот же движок на Rust — когда он вам нужен

Rust — полноценный язык для целых приложений: UI, логика и компоненты компилируются в нативный код, готовый к требовательной бизнес-логике. Оба языка используют одни и те же элементы и сигналы, а экраны на 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())),
        )
}

Затрагиваются только изменившиеся представления

Каждый элемент — это представление, которое хранит платформа. Изменение запускает только те выражения, которые читают изменившийся сигнал, одним пакетом за кадр. Главный поток никогда не ждёт JS, а нативные анимации работают без него, поэтому прокрутка и анимация держат 120 FPS, а в покое приложение почти простаивает.

Как сохраняется скорость

Меняются два текста, читающие сигналы, и в истории появляется строка; другие представления не затрагиваются.

$ 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. Проверить

    Проверьте типы, затем запустите его тесты в headless-режиме.

    nsh check nsh test
  3. Подписать

    Пакет .nshpkg с подписью Ed25519, ключ релиза которого сертифицирован офлайн-корнем.

    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
Встраивание по платформам

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.

Базовые компоненты без стилей

Навигация, диалоги, меню, поповеры, вкладки, селекты, таблицы данных, редактор кода и ядро rich text. Оформляйте каждую часть классами Tailwind или типизированными стилями.

nsh/base

Один тулчейн — для людей и агентов

Один исполняемый файл nsh создаёт, собирает, проверяет типы, делает hot reload, тестирует, упаковывает и подписывает. Декларации генерируются из исходников на Rust, а документация отдаётся как llms.txt, поэтому агент проходит весь цикл теми же командами.

nsh run, nsh test, nsh package

Проверка на каждой целевой платформе

Одни и те же тесты запускаются в headless-режиме, а также в настоящих окнах, симуляторе iOS, на устройстве Android или в браузере — со штатным вводом платформы.

nsh test --target ios

Нативные представления и взаимодействие,
ваш визуальный дизайн

Установите nsh и запустите своё первое приложение на TSX за минуту.