ump-monorepo
v0.0.1
Published
A cross-platform framework for building native apps with React-flavoured JSX. Write once, run on Android, iOS, HarmonyOS, and Web.
Readme
UMP — Universal Mobile Platform
A cross-platform framework for building native apps with React-flavoured JSX. Write once, run on Android, iOS, HarmonyOS, and Web.
UMP mirrors the React + React Native split:
ump-core≈react— platform-agnostic runtime: JSX runtime (auto, viaump-core/jsx-runtime), hooks, render, module / refresh registry.ump-native≈react-native— native components + platform glue (View / Text / Touchable / StyleSheet / web-standard globals / …). It does not re-exportump-core.
A single C++ runtime hosts the reconciler, style, layout (Yoga), render
(Skia), event, and HMR client. Hermes / JSC / JSVM / Browser sit behind
a JSEngine adapter.
Quick Start
For a guided walkthrough (scaffold → run on Android/iOS/Harmony/H5 →
HMR → device build), see docs/tutorials/quickstart.md.
For RN migrators, see docs/tutorials/migrate-from-rn.md.
The shell snippets below assume UMP_REPO points at your local clone:
export UMP_REPO=/path/to/your/umpAfter cloning, run ump doctor (from ump-cli) to verify the
toolchain — it checks for Xcode, Android SDK + NDK 26.1.10909125,
Node ≥ 18, and the vendored hermesc binary used to compile bytecode
for release builds. It also validates UMP_REPO: a hard fail if it is
unset or does not point at a UMP checkout, and a warning if
third_party/skia/lib is missing (native platforms/ builds resolve
UMP_ROOT from this env and need the Skia slices).
npm install ump-native ump-core
# CLI for scaffolding / dev-server / device run is shipped separately.
# Until ump-cli is published to npm, run it from the repo:
# node packages/ump-cli/src/index.js <subcommand>
# A `ump` shim that points at the cloned repo is the recommended setup.The TypeScript / JSX setup uses the automatic JSX runtime
(jsx: "react-jsx", jsxImportSource: "ump-core") — no per-file
import { createElement, Fragment } from 'ump-core' is required;
<View> and <></> resolve against ump-core/jsx-runtime.
// App.tsx
import { useState } from 'ump-core';
import { View, Text, Pressable, StyleSheet } from 'ump-native';
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
title: { fontSize: 24, fontWeight: 'bold' },
button: { padding: 16, backgroundColor: '#4299E1', borderRadius: 8 },
});
export default function App() {
const [count, setCount] = useState(0);
return (
<View style={styles.container}>
<Text style={styles.title}>Count: {count}</Text>
<Pressable style={styles.button} onPress={() => setCount(count + 1)}>
<Text style={{ color: '#FFF' }}>+1</Text>
</Pressable>
</View>
);
}// index.tsx — entry point
import 'ump-core'; // initialise module/refresh runtime
import { AppRegistry } from 'ump-native';
import App from './App';
AppRegistry.registerComponent('MyApp', () => App);
AppRegistry.runApplication('MyApp');Components
Legend: ✅ supported · ⚠️ JS-shape ready, native wiring partial / placeholder · ❌ not yet.
The components table now carries no ⚠️ rows — the per-platform
native-wiring follow-ups (originally tracked in
docs/superpowers/plans/2026-05-27-native-followup-6-items.md,
the trigger list) have all shipped, most recently RefreshControl's
pull-to-refresh gesture on all 4 platforms.
Picker / DatePicker previously carried ⚠️ marks waiting on
native UIPickerView / Spinner / UIDatePicker hand-offs. They
are now ✅: shipping a uniform JS composition (Pressable list /
numeric TextInput row) across all native platforms is the
intended design for UMP's mini-program / app-shell target — not a
"placeholder". Apps that need OS-native pickers can fork the
plugin and add a createNativeViewComponent host (see
packages/ump-plugins/video/native/ for the shape).
Layout & primitives
| Component | iOS | Android | Harmony | H5 | Notes |
|-----------------------|-----|---------|---------|-----|-------|
| View | ✅ | ✅ | ✅ | ✅ | Flexbox container |
| Text | ✅ | ✅ | ✅ | ✅ | Skia-rendered (not a native TextView). Supports letterSpacing / wordSpacing;native wordSpacing currently recognizes ASCII space only and complex grapheme shaping is not fully browser-equivalent. selectable is honored on h5 (CSS user-select: text) but is a no-op on iOS / Android / Harmony — UMP would need Skia-side selection state + per-platform copy menus, tracked as a roadmap item. A dev console.warn fires when you set it on native. |
| Image | ✅ | ✅ | ✅ | ✅ | Async loading + resizeMode |
| ScrollView | ✅ | ✅ | ✅ | ✅ | Imperative ScrollViewHandle |
| FlatList | ✅ | ✅ | ✅ | ✅ | Virtualised (verified 1000+ items) |
| SectionList | ✅ | ✅ | ✅ | ✅ | Section headers + virtualisation (via ump-plugin-sectionlist) |
| VirtualizedList | ✅ | ✅ | ✅ | ✅ | Generic data-source-agnostic windowing; backs FlatList / SectionList |
| IndexedStack | ✅ | ✅ | ✅ | ✅ | Persisted children, only one shown |
| SafeAreaView | ✅ | ✅ | ✅ | ✅ | Edges + insets prop |
| KeyboardAvoidingView| ✅ | ✅ | ✅ | ✅ | IME height shrinks layout |
| MaskedView | ✅ | ✅ | ✅ | ✅ | Composite mask (Skia layer on native, <svg> clipPath on H5) |
Inputs & controls
| Component | iOS | Android | Harmony | H5 | Notes |
|-----------------------|-----|---------|---------|-----|-------|
| Pressable | ✅ | ✅ | ✅ | ✅ | pressedStyle, hoverStyle, hitSlop, rippleStyle (Material press ripple — same Skia animator on all 3 native platforms; h5 is a v1 no-op) |
| TouchableOpacity | ✅ | ✅ | ✅ | ✅ | RN-shape wrapper over Pressable |
| TextInput | ✅ | ✅ | ✅ | ✅ | 8 keyboard props + capitalize / submit / focus. Text value + placeholder inherit letterSpacing / wordSpacing. Harmony maps keyboardType / returnKeyType to inputMethod.TextInputType.{NUMBER,EMAIL_ADDRESS,...} + EnterKeyType.{NEXT,SEARCH,...} via Index.ets UMPImeConfig listener (9a269a8) |
| Switch | ✅ | ✅ | ✅ | ✅ | |
| Slider | ✅ | ✅ | ✅ | ✅ | (via ump-component) |
| Picker / PickerItem| ✅ | ✅ | ✅ | ✅ | Ships a uniform Pressable list across all 4 platforms (H5 included) by design — UMP targets mini-program / app-shell apps where consistent cross-platform UX is preferred over per-OS native pickers. There is no platform branch, so H5 uses the same JS composition (not a native <select>). Apps that need UIPickerView / Spinner / Harmony wheel / <select> can wrap a createNativeViewComponent host (via ump-component) |
| DatePicker | ✅ | ✅ | ✅ | ✅ | mode='date'\|'time'\|'datetime'. Ships a numeric-TextInput row across all 4 platforms (H5 included) by design (mini-program UX consistency). There is no platform branch, so H5 uses the same JS composition (not a native <input type="date\|time\|datetime-local">). Apps that need UIDatePicker / android.widget.DatePicker / Harmony wheel / <input type=date> can wrap a native host (via ump-component) |
| RefreshControl | ✅ | ✅ | ✅ | ✅ | Spinner UI + controlled refreshing render, and the pull-to-refresh gesture is wired on all 4 platforms: native ScrollNode runs an over-pull state machine (begin_or_continue_overpull / release_overpull) and UMPRuntime fires onRefresh on armed release; H5 ScrollView runs an equivalent rubber-band pull gesture. Controlled — the control retracts only when the app sets refreshing false |
| Form / FormField | ✅ | ✅ | ✅ | ✅ | Pure-JS Flutter-style declarative form validation |
Surfaces & overlays
| Component | iOS | Android | Harmony | H5 | Notes |
|-----------------------|-----|---------|---------|-----|-------|
| Modal | ✅ | ✅ | ✅ | ✅ | Slide / fade animation |
| Dialog | ✅ | ✅ | ✅ | ✅ | |
| BottomSheet | ✅ | ✅ | ✅ | ✅ | Imperative BottomSheetHandle |
| StatusBar | ✅ | ✅ | ✅ | ✅ | Bar style + animation |
| ActivityIndicator | ✅ | ✅ | ✅ | ✅ | |
| ProgressBar | ✅ | ✅ | ✅ | ✅ | (via ump-component) |
| AnimatedSwitcher | ✅ | ✅ | ✅ | ✅ | Cross-fade between children |
| Toast | ✅ | ✅ | ✅ | ✅ | Short transient message, 3 durations |
| Alert | ✅ | ✅ | ✅ | ✅ | Title + message + buttons; native modal on each platform |
| Hero | ✅ | ✅ | ✅ | ✅ | Shared-element morph: source unmount snapshots its rect into HeroController; destination mount morphs from that rect to its own layout via Animated.timing on translate+scale (uniform). 280ms default; snapshots older than 30s drop |
Media & native views
| Component | iOS | Android | Harmony | H5 | Notes |
|-----------------------|-----|---------|---------|-----|-------|
| Canvas | ✅ | ✅ | ✅ | ✅ | Skia path / paint draw API. 2D text supports letterSpacing / wordSpacing for fillText / strokeText / measureText().width |
| Video | ✅ | ✅ | ✅ | ✅ | iOS AVPlayer + Android ExoPlayer via the plugin's native/cpp/video_manager.cpp; Harmony routes through UMPVideoHost.ets (ArkUI native-view registry) over media.AVPlayer (via ump-plugin-video) |
| Audio | ✅ | ✅ | ✅ | ✅ | Web-shape HTMLAudioElement / HTMLMediaElement family (the classes themselves stay in ump-native as runtime backbone); native player via the plugin's audio_manager.cpp per platform. controls prop is forwarded but UI must be built with Pressable + ProgressBar (no native chrome) (via ump-plugin-audio) |
| WebView | ✅ | ✅ | ✅ | ✅ | iOS WKWebView + Android JNI via the plugin's native/cpp/webview_manager.cpp; Harmony routes through UMPWebViewHost.ets. Security: injectedJavaScript / executeJs are eval-equivalent — never feed onMessage payloads back without sanitisation (via ump-plugin-webview) |
Cross-platform utilities (non-component exports)
| Symbol | Notes |
|-------------------------------|-------|
| Link | Routes via app scheme (in-app router) or Linking.openUrl (external) |
| Vibration | Tactile feedback. iOS pattern arrays degrade to a single tap per platform-hard-limit |
| Matrix4 | Column-major 4×4 affine transform helpers (@ohos.matrix4 parity) (via ump-plugin-matrix4) |
| LayoutAnimation | RN-shape configureNext surface. v1 records the config; native consumption is a follow-up |
| Worker / runOnWorker | Off-main-thread JS. Native ships per-Worker JSEngine isolates (Hermes / JSC / JSVM) and the runOnWorker runtime-worklet sugar; H5 re-exports the browser's native Worker (use postMessage directly — runOnWorker is native-only) (via ump-plugin-worker) |
Accessibility
label / role / state / hint / liveRegion / testID props are
accepted on every component. Native AT bridges (TalkBack /
VoiceOver / Harmony screen reader) are wired on all three platforms.
Harmony's harmony_a11y shim broadcasts announcements + node-level
property updates and queries is_screen_reader_active for runtime
gating; real-device validation pending.
API surface
Animation
Animated,AnimatedValue,Easing,timing,spring, composers (parallel/sequence/stagger/loop/delay),useAnimatedValue,createAnimatedComponent.- Reanimated (
SharedValue/useSharedValue/useAnimatedStyle, Tier-1) — sugar overAnimatedValuereusing the same C++ValueRegistryslot. The dev-server worklet plugin rewritesuseAnimatedStylecallsites at build time. H5 fallback runs the same surface overrequestAnimationFramewithuseState-driven re-renders; the build-time worklet plugin is platform-agnostic. (viaump-plugin-reanimated)
Gestures
Gesture recognizers: pan, press, scale, pinch, rotate.
GestureArena arbitrates between contenders. Multi-touch wiring
verified across all three native platforms — Harmony's NAPI
touchPoints[] loop, Android's ACTION_POINTER_DOWN/UP +
getPointerCount() JNI fan-out, and iOS's event.allTouches
with stable UITouch* → pointer_id mapping via NSMapTable.
Real-device multi-pointer recognizer validation still pending.
Streams
ReadableStream, WritableStream, TransformStream and their
default-controller / writer types — installed onto globalThis
non-overwriting.
Workers
Worker (postMessage / terminate, structured-clone payloads,
postTaskWithTransfer for ArrayBuffer hand-off, runOnWorker for
runtime worklet dispatch).
On native, each Worker owns its own JSEngine instance constructed
on the worker thread (Hermes / JSC / JSVM per platform) —
main-thread state is never visible from the worker. isAlive()
reports the engine-ready / terminated state; terminate() joins the
thread and tears the engine down before the next spawn.
On H5, the browser's native Worker is re-exported unchanged; pass
a data:application/javascript,... URL or a worker module URL and
use the standard postMessage / onmessage pair. The runOnWorker
sugar is native-only — calling it on H5 throws (use a real Worker +
postMessage in browsers).
Web Standards globals
All of these install on globalThis as a side-effect of importing
ump-native, non-overwriting (a host-provided implementation always
wins). Each is also re-exported as a named import.
| Group | APIs |
|--------------|------|
| Network | fetch, Headers, Request, Response, AbortController, AbortSignal, Blob, File, FormData. WebSocket (via ump-plugin-websocket); EventSource (via ump-plugin-eventsource) |
| Storage | localStorage, sessionStorage, Storage. indexedDB family — IDBDatabase, IDBObjectStore, IDBTransaction, IDBCursor (via ump-plugin-indexeddb) |
| Streams | ReadableStream, WritableStream, TransformStream (+ controllers / writers), TextEncoderStream, TextDecoderStream |
| DOM events | EventTarget, Event, CustomEvent, MessageEvent, ErrorEvent, PromiseRejectionEvent. CloseEvent (via ump-plugin-websocket) |
| Files | FileReader, URL.createObjectURL / revokeObjectURL |
| Encoding | TextEncoder, TextDecoder, atob, btoa |
| Crypto | crypto.getRandomValues, crypto.randomUUID, crypto.subtle.digest (SHA-1 / -256 / -384 / -512) |
| URL | URL, URLSearchParams |
| Scheduling | setTimeout / setInterval / clearTimeout / clearInterval, requestAnimationFrame / cancelAnimationFrame, requestIdleCallback / cancelIdleCallback, queueMicrotask |
| Performance | performance.now/mark/measure/getEntries…, PerformanceObserver (via ump-plugin-performance) |
| Observers | MutationObserver. IntersectionObserver, ResizeObserver (via ump-plugin-observers) |
| Channels | MessageChannel, MessagePort. BroadcastChannel¹ (via ump-plugin-broadcast-channel) |
| Cloning | structuredClone |
| Memory | WeakRef, FinalizationRegistry |
| Canvas | OffscreenCanvas, ImageBitmap, createImageBitmap, Element.animate() (Web Animations API) |
| Media (peer) | RTCPeerConnection, RTCSessionDescription, RTCIceCandidate, RTCDataChannel (JS-shape only, native peer connection is a follow-up) (via ump-plugin-webrtc) |
| Notifications| Notification (JS shim — request permission + show; permission gate per platform) (via ump-plugin-notification) |
| History | globalThis.history (pushState / replaceState / back / forward / go / popstate) |
| Viewport | globalThis.matchMedia, innerWidth, innerHeight, devicePixelRatio, visualViewport |
| Connectivity | navigator.onLine + online / offline events |
| navigator | clipboard, connection, geolocation, vibrate, userAgent, language, permissions, share |
| Internationalization | Intl (FormatJS-backed polyfill on hosts where Intl is missing or broken — Hermes 0.82) |
¹ BroadcastChannel on native is single-realm: messages reach
peers in the same JS realm only. The web spec also fans out across
same-origin tabs / iframes / workers — that cross-realm delivery is
a no-op on Android / iOS / Harmony because there is no equivalent IPC
layer. Note: each Worker (see Worker.ts) runs in its own JSI
isolate with its own module graph, so a channel created in a worker
does NOT see messages from one on the main thread; use the worker
handle's postMessage/onmessage pair for thread-to-thread comms.
Use a platform bridge (Android ContentProvider, iOS CFMessagePort,
Harmony RPC) for cross-process messaging. On the H5 peer the browser's
native BroadcastChannel is re-exported and works across tabs as
expected.
Strategy in one line: prefer web naming and shapes; only break out
of the ES-module pattern when the spec demands a global (fetch /
requestAnimationFrame / similar). Both module imports
(import { fetch } from 'ump-native') and the global
(globalThis.fetch) work, with the host-provided implementation
always winning when present.
Architecture
┌───────────────────────────────────────────────────────────┐
│ User code · JSX/TSX · StyleSheet │
├───────────────────────────────────────────────────────────┤
│ ump-native Components · Web-standard globals │
│ Animated · Reanimated · Gesture · Streams │
├───────────────────────────────────────────────────────────┤
│ ump-core createElement · hooks · render · Fragment │
│ module-runtime · refresh-runtime │
├───────────────────────────────────────────────────────────┤
│ ump-dev-server esbuild + __ump_define plugin │
│ refresh-inject · worklet · ws HMR server │
├───────────────────────────────────────────────────────────┤
│ C++ Runtime Reconciler · Style · Yoga · Skia · Event │
│ HmrClient (libwebsockets) · ValueRegistry │
├──────────┬─────────┬───────────┬──────────────────────────┤
│ Android │ iOS │ HarmonyOS │ H5 (Web) │
│ Hermes │ JSC │ JSVM │ Browser │
│ EGL+Skia │Metal+Skia│Vulkan+Skia│ DOM+CSS │
│ │ │ +raster │ │
└──────────┴─────────┴───────────┴──────────────────────────┘+raster on HarmonyOS: when Vulkan is unavailable (emulator) the
adapter falls back to CPU rasterisation directly into the
OHNativeWindow buffer via SkSurfaces::WrapPixels. Real devices keep
the Vulkan path.
The plugin model (ump-plugin-core) lets host apps register router /
storage / session-storage / biometrics / image-picker / share /
filesystem / permissions modules behind a uniform UMPPlugin
interface — autolinked into the native build by ump autolink and
into JS at runtime via PluginManager.
Project layout
runtime/ C++ core (platform-agnostic)
├── core/ Types, CommandQueue, BundleVerifier, UpdateManager
├── engine/ JSEngine abstraction + Hermes/JSC/JSVM adapters
├── reconciler/ Fiber tree, Diff, Hooks, JS Bridge, ValueRegistry
├── style/ CSS parser, StyleStore
├── layout/ Yoga Flexbox wrapper
├── render/ RenderNode, ViewNode, TextNode, Pipeline
├── event/ Touch, HitTest, Gesture recognizers, GestureArena
├── hmr/ C++ HMR client (libwebsockets, UMP_ENABLE_HMR)
└── platform/ PlatformAdapter + Android/iOS/HarmonyOS adapters
platforms/
├── android/ Native Android app (Skia)
├── ios/ Native iOS app (Metal + Skia)
├── harmony/ HarmonyOS app (Vulkan + Skia)
└── h5/ Web app (DOM + CSS, esbuild via ump-dev-server)
packages/
├── ump-core/ Platform-agnostic runtime (createElement, hooks, render)
├── ump-native/ Native components + web-standard globals
├── ump-h5-native/ H5 mirror of ump-native (DOM-backed)
├── ump-test-utils/ Snapshot + dts-surface helpers (private)
├── ump-dev-server/ esbuild build + Fast Refresh HMR + worklet plugin
├── ump-cli/ ump create/dev/build/run/doctor/publish/bundle-analyze
└── ump-plugins/ router / storage / biometrics / permissions /
svg / video / webview / audio / worker / webrtc /
notification / eventsource / websocket / observers /
indexeddb / performance / reanimated / sectionlist /
matrix4 / picker / slider / datepicker / progressbar /
broadcast-channel / …
examples/ 60+ demo apps (counter, FlatList, Reanimated, Worker,
Canvas, Video, WebView, BottomSheet, plugin/API demos, …)
scripts/ Build helpers
└── build-skia-ios.sh Cross-compile Skia for ios-arm64 + ios-arm64-simCLI
| Command | Description |
|---------|-------------|
| ump create <name> | Scaffold a new app project |
| ump create-plugin <name> | Scaffold a new plugin project |
| ump dev -p <platform> | Dev server with Fast Refresh HMR |
| ump build -p <platform> | Production build |
| ump run -p <platform> | Build and run on device/emulator |
| ump doctor | Check development environment |
| ump publish | Package signed bundle for dynamic update |
| ump autolink | Regenerate native manifests from plugins |
| ump plugin-lint | Lint plugin name / ump.native shape / events |
| ump plugin-link [path] | Pack a local plugin and install it |
| ump bundle-analyze | Per-package + per-module byte breakdown via esbuild metafile |
Fast Refresh
RN-style module-level reload that preserves useState when component
signatures are unchanged. See docs/hmr.md for the full
reload semantics.
node packages/ump-dev-server/bin.js # default port 8081
node packages/ump-dev-server/bin.js --hmr-port=8765Both Android and HarmonyOS emulators reach the host dev-server through
10.0.2.2. Harmony additionally needs ohos.permission.INTERNET in
module.json5, otherwise socket() returns EPERM.
TypeScript
The three ship packages (ump-core / ump-native / ump-h5-native)
all build to dist/:
cd packages/ump-core && npm run build:types
cd packages/ump-native && npm run build:types
cd packages/ump-h5-native && npm run build:typesEach plugin under packages/ump-plugins/* builds to its own lib/
via npm run build (the pretest hook auto-builds before tests).
Each package.json exposes main: dist/index.js, types:
dist/index.d.ts. prepublishOnly rebuilds dist/ before publish so
consumers always install JS, not TS. dist/ is gitignored — when
working from a fresh clone, run build:types in each package before
running tests / typecheck across workspaces.
Status
UMP has reached broad React-Native + Web-Standards parity at the
JS surface (Hermes 0.82.1 vendored on Android). Touch / Layout / Render / Style / IME backbone is
production-ready. The Tier-7 P0/P1 backlog of "JS-shape-ready, native
partial" gaps has been cleared in a 17-PR perf round and a 9-PR
native-depth round: Worker per-instance isolates, iOS/Android/Harmony
multi-touch dispatch, Harmony a11y / IME / KeyboardAvoidingView,
Reanimated H5 fallback, and Video / WebView native players are all
landed (Harmony Video/WebView route through the ArkUI native-view
registry — UMPVideoHost.ets / UMPWebViewHost.ets over
media.AVPlayer / webview.WebviewController — rather than the
plugin-side C++ managers used on iOS/Android, since OHOS exposes
these APIs only at the ArkTS layer; sources live under
packages/ump-plugins/{video,webview}/native/ after the carve-out).
The one
remaining bring-up step is the iOS Skia .a cross-compile — a
one-time local run of scripts/build-skia-ios.sh (now fully
automated) before the iOS target can link.
Detailed audit reports live under
docs/superpowers/audits/. The most
recent passes are:
2026-05-26-error-dx-prod-test-perf.md— P1/P2 dev experience.2026-05-27-security-api-docs-memory-parity.md— security + API stability + memory + parity.2026-05-27-tier7-recheck.md— re-verification of the Tier-7 follow-up status (95 commits since the 2026-05-22 native-depth pass).
Verified results
- H5 (Chrome, Puppeteer): counter PASS,
FlatListvirtualises 1000 items down to 23 visible, zero console errors. - Android (emulator, adb): counter / TextInput / Switch / FlatList
render;
useStatepreserved across Fast Refresh. - HarmonyOS (emulator 6.0.0.112, hdc): counter renders, CPU raster fallback engages when Vulkan unavailable, Fast Refresh end-to-end verified, no fault logs.
- iOS (Simulator, JavaScriptCore): counter renders and is interactive
(onPress increments on tap). Built via the CMake-generated Xcode project;
Skia iOS slices are produced once with
scripts/build-skia-ios.sh(see iOS — bring-up checklist).
Android — bring-up checklist
# Required: Android SDK + a specific NDK pinned by the runtime CMake.
# `ump doctor` will tell you if either is missing.
sdkmanager "platforms;android-34" "ndk;26.1.10909125"
# Optional but recommended: speeds up cold first build.
export ANDROID_HOME=$HOME/Library/Android/sdk # macOS default
# export ANDROID_HOME=$HOME/Android/Sdk # Linux default
# Connect a device or boot an emulator, then:
ump dev -p android # dev-server + adb push, HMR enabled
ump build -p android # release APK with hermesc bytecodeplatforms/android/app/build.gradle.kts picks the NDK version from
ANDROID_NDK_HOME (falling back to the default SDK path). If NDK
26.1.10909125 isn't installed, the configure step fails with a
gradle-side message pointing at the missing component.
iOS — bring-up checklist
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
# One-time prep already done locally; if you fresh-clone:
mkdir -p ~/work/skia-build && cd ~/work/skia-build
git clone --depth 50 --branch chrome/m149 https://skia.googlesource.com/skia
cd skia && python3 tools/git-sync-deps
brew install gn ninja
# Produce third_party/skia/lib/ios-arm64{,-sim}/*.a (~20 min)
bash $UMP_REPO/scripts/build-skia-ios.sh
# Generate the Xcode project with the iOS toolchain flags (there is no
# checked-in .xcodeproj — CMake produces it), then build / run:
cmake -G Xcode -S platforms/ios -B platforms/ios/build \
-DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_SYSROOT=iphonesimulator \
-DCMAKE_OSX_ARCHITECTURES=arm64 -DCMAKE_BUILD_TYPE=Debug -DUMP_ENABLE_HMR=OFF
cmake --build platforms/ios/build --config Debug
xcrun simctl install booted platforms/ios/build/Debug-iphonesimulator/UMPApp.app
xcrun simctl launch booted com.ump.app
# (or open platforms/ios/build/UMPApp.xcodeproj in Xcode and Run)platforms/ios/CMakeLists.txt picks ios-arm64-sim or ios-arm64
automatically based on CMAKE_OSX_SYSROOT. If libskia.a is missing
the project still configures (with a warning); only the link step
fails.
HarmonyOS — bring-up checklist
# Required: HarmonyOS SDK (HMS) + DevEco Studio.
# Download from https://developer.huawei.com/consumer/en/deveco-studio/
# Then expose:
export DEVECO_SDK_HOME=/Applications/DevEco-Studio.app/Contents/sdk
# Linux: ~/Huawei/DevEcoStudio/sdk
# Open the Harmony project in DevEco:
deveco-studio platforms/harmony/
# Or build from CLI via hvigorw (bundled with the SDK):
cd platforms/harmony && hvigorw assembleHapplatforms/harmony/entry/src/main/module.json5 declares the runtime
permissions UMP relies on (ohos.permission.INTERNET,
ohos.permission.GET_NETWORK_INFO, plus camera / location / mic /
read-media when the corresponding plugin is active). Edit that file —
not config.json — to add or remove permissions.
The Harmony peer ships a Vulkan-backed Skia path; on the emulator
Vulkan is unavailable and the runtime falls back to +raster
automatically. No manual flag needed.
Plugins
import { PluginManager } from 'ump-plugin-core';
import { routerPlugin } from 'ump-plugin-router';
import { sessionStoragePlugin } from 'ump-plugin-session-storage';
const pm = new PluginManager();
pm.register(routerPlugin({ routes: [/* … */] }));
pm.register(sessionStoragePlugin({ prefix: 'app' }));
pm.runAppDidMount(app);Persistent key/value storage uses the web-standard
globalThis.localStoragedirectly fromump-native— no plugin required. Useump-plugin-session-storagefor prefix-scoped session-lifetime storage on top ofglobalThis.sessionStorage.
Each plugin declares ump-core as a peerDependency so a single runtime
instance is shared across the host app and its plugins. See
packages/ump-plugins/ for the full list.
Reference docs
详细 API 文档:
docs/components/— 每个组件的 props / 方法 / 示例 / 平台差异。docs/api/— Animated / Linking / Vibration 等 ump-native 工具,web-standards 偏离点,以及 plugin 暴露的非组件 API。docs/css.md— StyleSheet 实际支持的 CSS 属性、单位、颜色、变换、过渡 / 动画、阴影,以及不支持的属性清单。
License
MIT
