API
nsh/device
The device API tells the app about the device and its environment. Rust code calls the functions of nsh_core::ui::prelude (window(), dark(), font_scale(), locale(), os(), idiom(), pixel_ratio(), set_color_scheme(), …); TSX imports nsh/device. The window size, the color scheme, the font scale, the pixel ratio and the locale are reactive: read them in a binding (a closure passed to an element in Rust, JSX in TSX), computed or effect, and what reads them updates when the platform reports a change (rotation, a window resize, switching to dark mode, changing the text size or the language, moving to a screen of another density).
use nsh_core::{
platform::{Appearance, Idiom, Os},
ui::prelude::*, // window, dark, font_scale, locale, os, idiom, pixel_ratio, set_color_scheme, ...
};import { colorScheme, fontScale, locale, pixelRatio, platform, setColorScheme, windowSize } from "nsh/device";See Device and environment for the guide.
#windowSize
const windowSize: Accessor<Size>;
The size of the surface in points: the window, or the embedded NshAppView / NshView. Reactive. Rust: window() -> Size<Points>.
let wide = move || window().width >= px(600.0);
view().style_when(wide, |s| s.flex_row())const wide = () => windowSize().width >= 600;
<View style={{ flexDirection: wide() ? "row" : "column" }}>{/* ... */}</View>#colorScheme
const colorScheme: Accessor<"light" | "dark">;
The appearance shown: the system’s, unless the app set its own (nsh.json "appearance", setColorScheme). Reactive. Rust: dark() -> bool; theme colors (AdaptiveColor) and the .dark(…) style variant follow it without reading it. Tailwind’s dark: variant follows it too (see Tailwind).
view().flex_1().bg(move || if dark() { black() } else { white() })const dark = () => colorScheme() === "dark";
<View style={{ flex: 1, backgroundColor: dark() ? "#000" : "#fff" }} />#fontScale
const fontScale: Accessor<number>;
The user’s preferred text size relative to the default (1 is the default size; Dynamic Type on iOS, font size on Android), or the text size the embedding app sets in its place. Reactive. Rust: font_scale() -> f32. Text already scales with it unless allowFontScaling={false}; read it when other sizes should follow, such as icons next to text.
#locale
const locale: Accessor<string>;
The user’s locale as a BCP 47 tag ("en-US", "zh-Hans-CN"), or the locale the embedding app sets in its place. Reactive. Rust: locale() -> Locale (locale().as_str() is the tag). Intl formats for it by default; read it to choose the strings of your own translations (Device and environment).
#setColorScheme
function setColorScheme(scheme: "light" | "dark" | "system"): void;
Shows the app in scheme whatever the system’s appearance, at once: colorScheme(), dark: classes, the components’ theme and the platform’s controls (alerts, the status bar’s default style) follow it; "system" follows the system again. nsh.json "appearance" sets it from the start. Rust: set_color_scheme(Appearance::Dark) (Appearance::Light, Appearance::System).
#platform
const platform: Platform;
interface Platform {
readonly os: "ios" | "android" | "macos" | "windows" | "linux" | "web";
readonly version: string;
readonly idiom: "phone" | "pad" | "mac" | "tv" | "desktop";
readonly catalyst: boolean;
select<T>(spec: PlatformSelectSpec<T>): T | undefined;
}
The platform the app runs on, constant while it runs.
| Member | Rust | Description |
|---|---|---|
os | os() -> Os (os().name() is the TS string) | The operating system. iOS, the iOS simulator and Mac Catalyst report "ios", browsers "web". |
version | os_version() -> (u16, u16) | iOS and desktops: the OS version ("17.5"); Android: the API level ("35"); the web: "0.0". Rust gives the major and minor version, or the API level and 0 on Android. |
idiom | idiom() -> Idiom; is_desktop() for "desktop" | The kind of device: "phone", "pad" (an iPad, an Android tablet), "mac" (Mac Catalyst in the Mac idiom), "tv" (Apple TV, Android TV) or "desktop", where desktop behavior applies: macOS, Windows, Linux, and browsers with a mouse and a wide window. |
catalyst | idiom() == Idiom::Mac | Mac Catalyst in the Mac idiom. |
select(spec) | match os() { … } | The value for this platform (see below). |
let columns = if is_desktop() { 3 } else { 1 };const columns = platform.idiom === "desktop" ? 3 : 1;#platform.select
interface PlatformSelectSpec<T> {
ios?: T;
android?: T;
macos?: T;
windows?: T;
linux?: T;
web?: T;
mobile?: T; // iOS, Android, and phone and tablet browsers
desktop?: T; // macOS, Windows, Linux, and desktop browsers
native?: T; // every platform but the web
default?: T;
}
Returns the value for the current OS (ios, android, web, …), else mobile / desktop, else native (every platform but the web), else default, else undefined. In a browser, mobile or desktop follows the idiom. Rust code matches on os() (and is_desktop() for mobile / desktop).
use nsh_core::platform::Os;
let card = view().p_4();
let card = match os() {
Os::Ios => card.shadow_color(black()).shadow_opacity(0.15).shadow_radius(px(8.0)),
Os::Android => card.elevation(px(4.0)),
_ => card,
};const shadow = platform.select({
ios: { shadowColor: "#000", shadowOpacity: 0.15, shadowRadius: 8 },
android: { elevation: 4 },
});#pixelRatio
const pixelRatio: Accessor<number>;
Device pixels per point (e.g. 3 on most iPhones). Reactive: it changes when the window moves to a screen of another density (on Mac Catalyst, the desktops, or an external display). Rust: pixel_ratio() -> f32, and round_to_nearest_pixel(points) for a length on whole device pixels; in TSX that length is Math.round(x * pixelRatio()) / pixelRatio().
let hairline = move || px(1.0 / pixel_ratio());const hairline = () => 1 / pixelRatio();#Layout direction
The layout direction follows the user’s language: in a right-to-left language (Arabic, Hebrew, …) the app lays out right to left, rows run from the right, and left / right style properties (marginLeft, paddingRight, …) mean start and end. Changes apply at once, with no restart.
A view’s direction style ("ltr", "rtl" or "inherit", the default) sets the direction of its own subtree; in Rust, .direction(LayoutDirection::..).
view().direction(LayoutDirection::Rtl).flex_row().gap_2().child(text("First")).child(text("Second"))<View style={{ direction: "rtl", flexDirection: "row", gap: 8 }}>
<Text>First</Text>
<Text>Second</Text>
</View>#Related
- Safe area insets (Rust
safe_area(); TSXsafeArea(),safeAreaInsets()) and the software keyboard (Rustkeyboard_height(); TSXkeyboardFrame()) are part ofnsh/base. - Screen reader and reduced motion settings are in
nsh/accessibility.