版本发布

发布说明

Native Shell 最近的版本及其 GitHub Release 说明,最新版本在最前。说明为 GitHub 上的英文原文。

在 GitHub 上查看全部版本

nsh v0.4.0

最新GitHub

Native Shell 0.4.0 fills out the unstyled component foundation with form controls, rich text and more chart marks; adds shader effects and transitions; runs web apps on the browser’s own JavaScript engine; and gives the APIs apps use one consistent shape.

Upgrading from 0.3.x? This release includes breaking API changes. See the migration notes below before rebuilding your app.

Highlights

  • Shaders across the UI. Draw a view’s box with WGSL using Shader, draw views and the whole screen through a program with ShaderEffect, ShaderTransition, ScreenEffect and ScreenTransition, and let pages and dialogs come and go through one with transitionShader. All of it draws on macOS, iOS, Android, Windows, Linux and the web; the GPUI backend draws Shader fills. (#517, #682, #763, #764, #765, #766, #767, #768, #769, #770)
  • A full set of form controls. Build forms and toolbars with Select, Combobox, Slider, NumberInput, OtpInput, DatePicker, Calendar, TimeField, ColorPicker, Toggle / ToggleGroup, Toolbar, Link, Pagination, Avatar / AvatarGroup and Accordion. They provide behavior, state and accessibility; your app supplies their look. (#536, #585, #510, #534, #535, #598, #577, #621, #702, #624, #690, #706, #610)
  • Rich content and navigation. Edit documents with RichEditor, a ProseMirror-style editor with tables; add a command palette with Command, nested lists with Tree, slides with Carousel, hover previews with HoverCard and scroll bars in your own look with Scrollbar. TextView typesets formulas out of the box. (#494, #518, #785, #725, #722, #697, #579, #841)
  • More charts from nsh/base/plot. Compose Bar, Pie, Arc, Sankey, Treemap, Dot, Cell, Rule, RadialLine and RadialGrid marks with PlotAxis and PlotLabel, color marks by category, plot against a second y scale, and move a plot’s hover with the keyboard and screen readers. (#505, #501, #601, #867, #829, #861, #819, #596, #549, #599, #592, #839, #521)
  • Web apps on the browser’s own engine. Every web app now runs on the browser’s JavaScript engine, in development with hot updates too. The runtime loads its parts on demand, which takes the gallery’s first load from 13.03 MB to 4.35 MB (4.01 MB to 1.41 MB gzipped), and published sites carry Brotli and gzip copies of their files. (#541, #603, #853)
  • More of the system and the toolchain. Follow whether the app is in use with lifecycle(), recognize speech with Speech.start on macOS and iOS, read shipped files with fetch on asset:/ URLs, and use WOFF and WOFF2 fonts with fallback along fontFamily lists. Rust apps gain file dialogs, system paths, window and menu APIs and svg(); an app’s crate can register native components in its generated hosts; and nsh publish <package> signs an existing package again with another release key. (#747, 0520080, #746, #745, #773, #629, #674, #683, #886)

Performance and platform improvements

  • Layout rounds to device pixels, frames that stay on their pixels stay unsent, and a subtree that moves by whole pixels skips its frame walk. Animation graphs evaluate each node once a frame and find interpolation segments by bisection. (#557, #617, #637, #588, #589, #590)
  • Images load through one bounded pipeline on the desktops, taking peak memory from about 1.3 GB to about 370 MB when scrolling 1,000 remote photos as thumbnails on Linux. Pasting 474 KB of unbroken text into a multiline Input takes 58 ms where it took 12 s on macOS, and DataTable, Editor, Select, Calendar and the input controls do less work as they scroll and take keys. (#674, #627, #622, #604, #638, #656, #817, #672)
  • iOS keeps focused inputs above the keyboard and launches on iOS 15 to 17, adds leading and SF Symbol bar buttons, draws backdrop blur behind a view’s background, and draws system alerts and the TabBar in the app’s theme colors. (#821, #888, #809, #866, #742, #741)
  • Android fixes wss:// WebSockets, follows display density changes while the app runs, keeps its runtime classes in minified embedding apps, strips release libraries, and improves keyboard and input handling. (#530, #431, #639, #538, #714, #715, #717, #836)
  • macOS draws fades without redrawing their views, taking one app’s memory footprint from 92 MB to 27 MB, and adds trusted web view input with sendInput and web view downloads. Linux draws images at device pixels on fractional scales, Windows speeds up plots and gives the wheel to native components, and GPUI delivers files dragged in from other applications and adds see-through windows. (#877, #895, #743, #758, #834, #879, #712, #591, #876)
  • The web applies state styles, platform props and scroll views’ contentStyle and contentClass, and passes every event payload field to the app. Selections stay intact across virtual list rows on every platform, arrow keys follow right-to-left layouts, and parts style with aria-expanded: and data-* attributes. nsh test compares values as Jest does. (#865, #885, #883, #892, #703, #797, #556, #815, #711)

Migrating from 0.3.x

0.4.0 gives each kind of API one shape: styles in CSS’s properties, one unit per quantity, one object per event, objects as return values, on(event, listener) for listeners, and one way to report failures. The old forms are removed. (#495, #524, #533, #558, #593, #612, #651)

Move styling props into style and use CSS’s shadows. Classes reach all of them too:

- <View style={{ shadowColor: "#000", shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.2, shadowRadius: 4 }} />
+ <View style={{ boxShadow: "0 2px 4px rgb(0 0 0 / 0.2)" }} />
- <Text style={{ textShadowColor: "#000", textShadowOffset: { width: 0, height: 1 }, textShadowRadius: 2 }}>…</Text>
+ <Text style={{ textShadow: "0 1px 2px #000" }}>…</Text>
- <Text numberOfLines={2} ellipsizeMode="middle" selectable>…</Text>
+ <Text style={{ numberOfLines: 2, textOverflow: "ellipsis-middle", userSelect: "text" }}>…</Text>
- <View pointerEvents="box-none" />
+ <View style={{ pointerEvents: "box-none" }} />
- <Image resizeMode="contain" />
+ <Image style={{ objectFit: "contain" }} />

Every event handler takes one object, and mouse events carry the DOM’s names:

- <Checkbox onChange={(checked) => …} />
+ <Checkbox onChange={({ checked }) => …} />
- <Input onChange={(text) => …} />
+ <Input onChange={({ value }) => …} />
- <Popover onOpenChange={(open, reason) => …} />
+ <Popover onOpenChange={({ open, reason }) => …} />
- onLayout={(bounds) => bounds.size.width}
+ onLayout={({ bounds }) => bounds.size.width}
- onScrollEnd={(offset) => …}                 // and the drag and momentum events
+ onScrollEnd={({ offset }) => …}
- onLoad={(size) => …}                        // Image
+ onLoad={({ size }) => …}
- onContentSizeChange={(size) => …}
+ onContentSizeChange={({ size }) => …}
- e.translationX, e.translationY, e.velocityX, e.velocityY, e.valueX, e.valueY    // PanEvent
+ e.translation.x, e.translation.y, e.velocity.x, e.velocity.y, e.value?.x, e.value?.y
- keymap.bind("mod+k", (keystroke) => …)
+ keymap.bind("mod+k", ({ keystroke }) => …)
- onHoverIn={…} onHoverOut={…} onHoverMove={(e: HoverEvent) => …}
+ onMouseEnter={…} onMouseLeave={…} onMouseMove={(e: MouseEvent) => …}

Functions return objects, options come as one object, and each element has a ref type of its own (ViewRef, InputRef, ScrollRef, ListRef, WebViewRef, VideoRef, NativeComponentRef):

- const finished = await value.timing(1);     // and spring, decay, keyframes, play, start
+ const { finished } = await value.timing(1);
- const last = await value.stop();
+ const { value: last } = await value.stop();
- const id = await AlertDialog.show({ actions: [{ label: "OK" }] });
+ const { action } = await AlertDialog.show({ actions: [{ id: "ok", label: "OK" }] });
- const opened = await openUrl(url);
+ const { opened } = await openUrl(url);
- const count = await editor.replaceAll(text);
+ const { count } = await editor.replaceAll(text);
- if (await Image.prefetch(src)) …
+ await Image.prefetch(src);                  // rejects on failure
- Window.setTitleBar("hidden", 40);
+ Window.setTitleBar({ style: "hidden", height: 40 });
- net.connect(host, port);
+ net.connect({ host, port });
- let node: YNode;
+ let node: ViewRef;
- BackHandler.onBackPress(listener);
+ BackHandler.on("press", listener);
- const sub = host.on(name, listener); sub.remove();
+ const stop = host.on(name, listener); stop();

Update these units and names:

- gestureState.vx                             // points per millisecond, also vy
+ gestureState.vx                             // points per second, also vy
- keyboardFrame().duration                    // seconds
+ keyboardFrame().duration                    // milliseconds
- viewAreaCoveragePercentThreshold: 50, itemVisiblePercentThreshold: 50
+ viewAreaCoverageThreshold: 0.5, itemVisibleThreshold: 0.5
- ImageSize
+ PixelSize

Use the websocket module’s own connect, which ends reads cleanly:

- import { WebSocket } from "websocket"; const c = await WebSocket.connect(url);
+ import { connect } from "websocket"; const c = await connect(url);
- await c.read()                              // rejected on a clean close
+ await c.read()                              // null after a clean close

Also update these APIs:

  • Handle failures by rejection and code: every function returning a promise rejects, a denied capability is an Error with code: "ERR_ACCESS_DENIED", permission and resource (fetch keeps a TypeError with the same fields), and platforms and quotas report ERR_FEATURE_UNAVAILABLE_ON_PLATFORM and ERR_NSH_QUOTA_EXCEEDED. Clipboard and Picker reject where they are not granted, and new WebSocket to a URL not granted fires error, then close (1006). (#651)
  • Read signal beside code in process.run’s output; code is null when a signal ended the child. (#651)
  • Handle Clipboard.readImage rejecting over 48 MB and on Android, and Clipboard.writeImage rejecting on Android. (#701)
  • Add flexDirection: "column" to a view that sets display: "flex" and stacks its children: that display lays them out in a row, as in CSS. (#495)
  • Use start, end, marginStart, paddingEnd and their relatives for sides that follow the direction: left, right, paddingLeft, textAlign: "left" and their relatives keep their side right to left, as in a browser. (#495)
  • Give Window frames the window’s top-left corner, y down from the primary screen’s top, on every platform (macOS used the bottom-left corner), and drop anchorTop. (#698)
  • Check platform.desktop or platform.idiom === "desktop" for the desktop idiom: idiom is "desktop" exactly where platform.desktop is true, and platform.catalyst is true under Mac Catalyst in every idiom. (#705)
  • Leave focusable unset on views that are not Tab stops: focusable={true} makes any view one. (#503)
  • Expect arrow keys in Tabs and RadioGroup: only the group’s stop is focusable, and Tab’s onPress and Radio’s onChange also fire when the keys move the focus. (#537)
  • Accept (error, reset) in every function given as ErrorBoundary’s fallback: each one is called, whatever parameters it declares. (#685)
  • Keep ios={{…}} and desktop={{…}} to their typed keys: an unknown key fails the type check. (#667)
  • Size elements in a component’s slots yourself where they relied on stretching: a slot takes its element’s own box, laid out as the component’s item. (#606)
  • Set your theme’s primary to choose TabBar’s selected color, which now follows that token. (#741)
  • Use nsh run --port <port> or --port 0 to run apps side by side: a plain nsh run replaces the NSH session on its port. (#428)
  • Add --abi arm64-v8a,armeabi-v7a,x86_64 to nsh package --target android when you ship x86_64: the default is arm64-v8a and armeabi-v7a, and --run still adds x86_64 on an x86_64 emulator. (#539)
  • On the web, call await player.runModule(entryURL, { appId, capabilities, chunks }) in place of player.run(bytecode, …) on custom pages, and import List, Image, Canvas, WebView and Video from nsh in place of their former module paths. App code runs in the page’s realm, so browser APIs follow the browser’s CORS, permission and secure-context rules. (#541, #603, #700)
  • In nsh/test, import PickRequest in place of Pick, allow for app.clock being null, write text: before literal text shaped like a selector prefix, handle ERR_NSH_TEST_UNSUPPORTED for open options a --target cannot honor, expect Jest’s toBe and toEqual, and pass network: "live" for real sockets and WebSockets. (#684, #708, #710, #711, #719)

In Rust, update these as well:

- text(s).ellipsize_mode(EllipsizeMode::Middle).selectable(true)
+ text(s).text_overflow(EllipsizeMode::Middle).select_text()
- image(src).resize_mode(ResizeMode::Contain)
+ image(src).object_fit(ResizeMode::Contain)
- .on_hover_in(|_: &()| ..) .on_hover_out(|_: &()| ..) .on_hover_move(|e: &HoverEvent| ..)
+ .on_mouse_enter(|e: &MouseEvent| ..) .on_mouse_leave(|e: &MouseEvent| ..) .on_mouse_move(|e: &MouseEvent| ..)
- .action("OK") .cancel("Cancel") .destructive("Delete")
+ .action("ok", "OK") .cancel("cancel", "Cancel") .destructive("delete", "Delete")
- area().y0(0.0)
+ area().y0(AreaY0::from(0.0))
- Scale::Point, Scale::Linear { domain }
+ Scale::Point { reverse: false }, Scale::Linear { domain, reverse: false }
- nsh_core::element::ambient::watch_scroll
+ nsh_core::element::watch_scroll
- nsh-core = "0.3"
+ nsh-core = { version = "0.4", features = ["declarations"] }    # where you read declarations
  • Take Size<DevicePixels> in image().on_load, and set Viewability’s view_area_coverage_threshold and item_visible_threshold as Percentage. (#524)
  • Pass line_height a LineHeight, and handle Prefetch’s Result<(), String>. (#495, #593)
  • Forward a generic helper’s listener to on_press as IntoListener<PressEvent, M>. (#629)
  • Read GridProps’ x and y as Prop<Option<Vec<f64>>>, and end full Environment struct literals with ..Default::default(). (#839, #747)
  • Build nsh-platform-web with its browser feature, and drop Bundle::with_harden_bytecode and harden_bytecode. (#603)

The compatibility baseline is still being prepared; 0.4.0 is not a frozen compatibility release.

Downloads

Prebuilt toolchains are available below for macOS Apple Silicon, Linux x86_64 and Windows x86_64, alongside the iOS and Android embedding SDKs. Each archive has a .sha256 checksum file.

Documentation and live demos · Full changelog

All merged pull requests

What’s Changed

New Contributors

Full Changelog: https://github.com/longbridge/nsh/compare/v0.3.0…v0.4.0

nsh v0.3.0

GitHub

Native Shell 0.3.0 expands the unstyled component foundation with editors, tables, dockable workspaces and charts; improves layout, scrolling and animation; and brings a simpler module structure to Rust and TSX apps.

Upgrading from 0.2.x? This release includes breaking API changes. See the migration notes below before rebuilding your app.

Highlights

  • Editors and data-heavy interfaces. Build code editing and review tools with Editor and DiffView, large tables with DataTable and Table, and dockable workspaces with DockArea, ResizablePanelGroup and ResizablePanel. These components provide behavior and state; your app supplies their look. (#321, #452, #458)
  • More UI building blocks. Add expandable content with Collapsible, task progress with Progress, notifications with Toast / ToastStack / ToastManager, and charts with Plot, Line, Area and Grid from nsh/base/plot. (#347, #352, #386, #403)
  • Richer motion and scrolling. Compose keyframes, motion presets and createMotion; use sticky positioning and animated sticky offsets; load more items at the start of a list; and draw custom pull-to-refresh indicators. Desktop and iOS animations now draw at the display’s refresh rate. (#387, #374, #376, #369, #433, #436)
  • Closer integration with embedding apps. Exchange messages through the host channel, route external URLs through the host, override embedded theme tokens on mobile, and use the system tab bar through TabBar from nsh/mobile. (#378, #388, #377, #423)
  • More resilient screens and images. Isolate rendering failures with ErrorBoundary, pick photos as native files, display data: and blob: image sources, position images with objectPosition, and reuse downloaded images from disk across launches. (#370, #390, #375, #371, #383)

Performance and platform improvements

  • Layout updates visit only the parts a change can move, unchanged text measurements avoid repeated layout, and lists bind visible rows before pacing their overscan. (#368, #410, #328)
  • iOS gains Liquid Glass, selectable system materials for backdrop blur, sharper images on 3× screens, and fixes for swipe-back navigation and keyboard interaction. (#338, #359, #362, #430, #443)
  • Android adds armeabi-v7a runtime builds and 16 KB page-size alignment, with fixes for back handling, native component cleanup and text input. (#420, #422, #341, #340, #421, #480)
  • Desktop and web fixes improve scrolling, focus, shortcuts, text rendering and accessibility. nsh check and nsh test now also run Cargo checks and tests for apps with Rust crates. (#327, #302, #322, #405, #434)

Migrating from 0.2.x

0.3.0 gives each API one home: primitives, environment and capabilities in nsh; unstyled functional components in nsh/base; and platform-specific APIs in nsh/mobile and nsh/desktop. The old import paths are removed. (#488)

- import { Clipboard } from "nsh/clipboard";
+ import { Clipboard } from "nsh";
- import { Input, type InputProps } from "nsh";
+ import { Input, type InputProps } from "nsh/base";
- import { Keyboard, safeAreaInsets } from "nsh/base";
+ import { Keyboard, safeAreaInsets } from "nsh";
- import { BackHandler } from "nsh/android";
- import { InputAccessoryView } from "nsh/ios";
+ import { BackHandler, InputAccessoryView } from "nsh/mobile";

The former nsh/animated, nsh/device, nsh/gesture, nsh/clipboard, nsh/host, nsh/picker, nsh/storage, nsh/keymap, nsh/native, nsh/accessibility, nsh/image, nsh/list, nsh/canvas, nsh/webview and nsh/video exports now come from nsh.

Use CSS environment values for safe-area and keyboard spacing, and View selectable for text selection:

- <SafeAreaView edges={["top"]}>…</SafeAreaView>
+ <View style={{ paddingTop: "env(safe-area-inset-top)" }}>…</View>
- <KeyboardAvoidingView behavior="padding">…</KeyboardAvoidingView>
+ <View class="pb-[env(keyboard-inset-height)]">…</View>
- <SelectionArea>…</SelectionArea>
+ <View selectable>…</View>

Also update these APIs:

  • Pass a Tooltip element to tooltip in place of a string.
  • Use Toast / ToastManager from nsh/base in place of Android’s Toast.show.
  • Use platform.os in place of the former iOS / Android isAvailable exports.
  • In Rust, replace nsh_core::ui imports with nsh_core::element, use nsh_core::prelude, and rename element::element to element::builder. Use nsh_base::{nav_stack, nav_page, dialog, alert_dialog} for navigation and dialogs.

The compatibility baseline is still being prepared; 0.3.0 is not a frozen compatibility release. (#496)

Downloads

Prebuilt toolchains are available below for macOS Apple Silicon, Linux x86_64 and Windows x86_64, alongside the iOS and Android embedding SDKs. Each archive has a .sha256 checksum file.

Documentation and live demos · Full changelog

All merged pull requests
What’s Changed

New Contributors

Full Changelog: https://github.com/longbridge/nsh/compare/v0.2.0…v0.3.0

Native Shell v0.2.0

GitHub

Native Shell 0.2.0 adds nsh/base, unstyled base components for navigation, dialogs, controls, popups and documents, and a capability-controlled set of standard modules (fs/promises, process, net, websocket, crypto and more). nsh test runs an app’s tests headless or on every platform, and the theme is now shadcn/ui’s tokens. It also replaces the React Native–shaped API with Native Shell’s own names: see Breaking Changes, and rebuild apps packaged for 0.1.

Components

  • Base components: nsh/base holds unstyled components that own behavior, state, focus and accessibility and leave every visual choice to the app: NavStack / NavPage, Dialog and AlertDialog. Rust apps present dialogs and navigation from nsh_base too. (#167, #236)
  • Controls: Switch, Checkbox (checked, unchecked or indeterminate), Radio / RadioGroup and Tab / Tabs. Style their states with Tailwind’s checked: or checkedStyle. (#256, #259, #276, #296, #192)
  • Popups: the Overlay primitive places content against an anchor; Popover, Menu / ContextMenu, Tooltip and Sheet are built on it. Any view can open its own menu on a secondary click. (#253, #143, #202)
  • TextView: render Markdown and HTML documents, with selection across texts. (#227)
  • Theme: the theme is shadcn/ui’s tokens (neutral by default), with the app’s own tokens and token(); Tailwind classes name the tokens (bg-primary, text-muted-foreground). (#304, #207, #142)

Runtime and modules

  • Standard modules: eleven bare imports, buffer, console, crypto, fs/promises, net, os, path, process, url, websocket and zlib, with grants, quotas and cancellation checked in Rust. All of them load on the web, where files live in app-private OPFS. (#145)
  • Storage: nsh/storage keeps an app’s data on the device, per app id. (#110)
  • Clipboard: read and write images and copied files. (#151)
  • Async data: resource loads data asynchronously, in TSX and in Rust. (#179)
  • Host functions: apps call functions the embedding app registers at run time. (#148)
  • Effects run the function they return as their cleanup; refs work on signals and fields; unhandled promise rejections are logged as browsers report them. (#183, #175, #163)

Rust components and apps with Rust crates

  • Rust classes: TypeScript uses Rust types as classes with new and calls Rust without JSON; only pub functions are exported, with clearer TypeScript. (#87, #155)
  • App crates: an app lists one or several Rust crates, each imported on its own, built from the nsh its crates depend on and without a command line of their own; Rust components nest on one host, modules group functions under a function, and components get state styles. (#86, #88, #154, #136, #203, #237)
  • Web: apps with Rust components package and run on the web, and their crates can use wasm-bindgen. (#78, #228)
  • Templates: nsh init --template ts|rust|mixed, each with a test of its own. (#146, #267, #310)

Testing and tooling

  • nsh test: runs an app’s *.test.tsx headless, on a manual clock when asked, and replaces nsh trace. (#187, #188, #189, #205)
  • On real platforms: nsh test --target runs the same tests with native input in GPUI windows, on Linux and Windows hosts, in an iOS simulator and on Android devices. (#223, #232, #234, #242)
  • Bundler: nsh.json chooses import conditions and aliases; apps bundle without reading tsconfig.json; named imports a native module does not export, text outside a Text and components that take their props apart are reported. (#138, #164, #172, #176, #177, #186)
  • NSH’s generated files live in .nsh, and nsh package writes to .nsh/out. nsh run configures development windows and runs Git-backed apps; source installs show their Git revision. (#219, #115, #213, #220)

Elements, layout and styles

  • Imports: every primitive is exported from nsh (View, Text, Scroll, Input, Overlay, Image, List, Canvas, WebView, Video). (#240)
  • CSS layout: grids and gradients lay out as CSS does; overflow: "hidden" clips in layout; views inside text follow verticalAlign. (#181, #258, #295)
  • Images: SVG images on every platform, and the @1x, @2x or @3x variant that fits the screen. (#230, #229)
  • Input: input method compositions are reported and kept going; mouse movement, onHoverMove and file drops on desktop views. (#222, #113, #141, #93)
  • Focus and accessibility: focusable keeps a pressable view out of the keyboard focus; disabled views are announced as disabled. (#139, #195)
  • Lists: sticky headers stick a distance from the top, and stay pinned with their recycled cells in place. (#285, #280, #281)

Platforms and embedding

  • GPUI is the backend for embedding in GPUI / GPUI Kit apps; standalone apps run on each OS’s own backend (AppKit, Win32, GTK 4). Embedding apps set VM limits and connect to a dev server. (#217, #99, #173)
  • iOS: the embed SDK ships as dynamic frameworks. (#244)
  • macOS: native desktop title bars, graceful shutdown, keyboard focus across pressable views and closing modals, transparent web views and import() chunks served from a directory. (#92, #91, #94, #95, #140, #116, #211)
  • Desktop: scroll indicators appear near their thumbs on hover; Window.orderFront and setFrame’s anchorTop; one copy of the desktop backends’ shared logic lives in the core. (#224, #215, #114, #218)
  • Web: published pages fetch each file once; scroll snapping reaches the final interval. (#252, #157)
  • Embedding: hosts set their apps’ locale and text size; only embedded apps are limited, and nsh run --limits acts as a host. (#150, #169)
  • Packages: nsh publish sets a package’s minimum runtime; packages from newer signers run on older runtimes; hosts accept a package once it proved healthy; errors carry their kind, stack and package, with positions mapped offline. (#101, #103, #137, #104, #106)

Performance

  • A screen’s and a modal’s content is built once, one changing child updates without remaking its siblings, and accessibility writes only the attributes that changed. (#119, #174, #118)

Breaking Changes

Base components move to nsh/base (#167)
-import { Stack, Screen, Modal, createNavigator, Alert } from "nsh/ui";
+import { NavStack, NavPage, Dialog, createNavStack, AlertDialog } from "nsh/base";
-<Stack navigator={nav}>{(r) => <Screen title="Home" preventBack={dirty()} onBackRequest={ask}>…</Screen>}</Stack>
+<NavStack state={nav}>{(r) => <NavPage title="Home" header={<MyHeader />} popOnBack={!dirty()} onBackPress={ask}>…</NavPage>}</NavStack>
-<Modal visible={open()} onRequestClose={() => open.set(false)} transparent>…</Modal>
+<Dialog open={open()} onOpenChange={open.set} class="m-auto w-80 rounded-xl bg-white" backdropClass="bg-black/50">…</Dialog>
-<View activeOpacity={0.6} accessibilityRole="button" onPress={go} />
+<View activeStyle={{ opacity: 0.6 }} role="button" onPress={go} />
-<Input onChangeText={set} onSubmitEditing={send} secureTextEntry />
+<Input onChange={set} onSubmit={send} masked />
-<List ListHeaderComponent={<Title />} contentContainerStyle={{ padding: 16 }} />
+<List header={<Title />} contentStyle={{ padding: 16 }} />

NSH draws no look of its own: desktop and web apps that relied on NSH’s page header give their pages a header.

Native Shell’s names for props, events and modules (#284, #283, #274)
-<Scroll scrollEnabled={false} showsIndicator={false} onMomentumScrollEnd={done} />
+<Scroll scrollable={false} scrollbar={false} onMomentumEnd={done} />
-<List onEndReached={more} onEndReachedThreshold={0.5} />
+<List onLoadMore={more} loadMoreThreshold={0.5} />
-<View onLayout={(e) => width.set(e.width)} />
+<View onLayout={(bounds) => width.set(bounds.size.width)} />
-<Image src={{ uri: url }} defaultSource={logo} />
+<Image src={url} placeholder={logo} />
-<WebView source="https://example.com" />
+<WebView src="https://example.com" />
-Platform.OS; PixelRatio.get(); Clipboard.setString(t);
+platform.os; pixelRatio(); await Clipboard.writeText(t);

Event payloads use typed geometry (e.offset.y, e.position.x, e.size.width), and window.ReactNativeWebView.postMessage is window.nsh.postMessage. The pull requests list every rename.

shadcn/ui theme tokens (#304)
-{ "themes": [{ "mode": "dark", "radius": 12, "colors": { "primary.background": "[#7](https://github.com/longbridge/native-shell/pull/7)c3aed" } }] }
+{ "cssVars": { "theme": { "radius": "0.75rem" }, "dark": { "primary": "[#7](https://github.com/longbridge/native-shell/pull/7)c3aed" } } }
-class="bg-danger text-link bg-list-head"
+class="bg-destructive text-primary bg-muted"
nsh.json needs an id to package and publish (#110)
 {
   "name": "Orders",
+  "id": "com.yourcompany.orders"
 }
Grids stretch their auto tracks as CSS does (#181)

A grid with a definite size and no alignContent / justifyContent shares its size among its auto tracks; alignContent: "flex-start" keeps the old layout.

Command line (#189, #232, #217)
-nsh trace App.tsx --tap <SELECTOR>
-nsh run --script <STEPS>
+echo '<steps>' | nsh test App.tsx --trace
+echo '<steps>' | nsh test App.tsx --target macos
-nsh package my-app --target gpui
+nsh package my-app --target macos   # or windows, linux
Rust API (#207, #304, #169, #236)
-nsh_core::ui::ambient::theme().primary
+nsh_base::theme().primary
-Limits::standalone_desktop() / Limits::standalone_mobile()
+Limits::standalone()
-stack().child(screen().title("Home"))
+nav_stack().child(nav_page().title("Home"))

New Contributors

Full Changelog: https://github.com/longbridge/native-shell/compare/v0.1.1…v0.2.0

nsh v0.1.1

GitHub

What’s Changed

New Contributors

Full Changelog: https://github.com/longbridge/nsh/compare/v0.1.0…v0.1.1

nsh v0.1.0

GitHub

What’s Changed

New Contributors

Full Changelog: https://github.com/longbridge/native-shell/commits/v0.1.0