native-fn
v1.2.0
Published
TypeScript library bridging Web APIs to native device capabilities — Picture-in-Picture, Fullscreen, Clipboard, Geolocation, Battery, Notifications, Vibration, Platform detection, and more.
Downloads
1,549
Maintainers
Readme
native-fn API Reference
Installation
npm
npm i native-fnyarn
yarn add native-fnunpkg
<script src="https://unpkg.com/native-fn"></script>jsdelivr
<script src="https://cdn.jsdelivr.net/npm/native-fn"></script>Table of Contents
- appearance
- badge
- battery
- clipboard
- dimension
- fullscreen
- geolocation
- notification
- open
- permission
- pip
- platform
- theme
- vibration
appearance
Signature
get value(): AppearancesReturns the current color scheme of the device.
Example
console.log(Native.appearance.value); // 'dark' | 'light' | 'unknown'Returns
Appearancesenum Appearances {
Unknown = 'unknown',
Light = 'light',
Dark = 'dark',
}Signature
onChange(listener: (appearance: Appearances) => void, options?: AddEventListenerOptions): () => voidSubscribes to device color scheme changes.
Example
const unsubscribe = Native.appearance.onChange((appearance) => {
console.log(appearance); // 'dark' | 'light'
});
unsubscribe();Returns
() => void// call to remove the listener
unsubscribe();badge
Signature
get supported(): booleanReturns whether badge is supported in the current environment.
Example
if (Native.badge.supported) {
await Native.badge.set(5);
}Returns
boolean// true — badge API support,
// false — no badge supportSignature
set(contents: number): Promise<void>Sets the app badge count.
Example
await Native.badge.set(5);Returns
Promise<void>Throws
throw new NotSupportedError // navigator.setAppBadge unavailableSignature
clear(): Promise<void>Clears the app badge.
Example
await Native.badge.clear();Returns
Promise<void>Throws
throw new NotSupportedError // navigator.setAppBadge unavailablebattery
Signature
get supported(): booleanReturns whether battery is supported in the current environment.
Example
if (Native.battery.supported) {
const battery = await Native.battery.value;
console.log(battery.level); // 0.0 – 1.0
}Returns
boolean// true — battery API support,
// false — no battery supportSignature
get value(): Promise<BatteryManager>Returns the current battery status.
Example
const battery = await Native.battery.value;
console.log(battery.level); // 0.0 – 1.0
console.log(battery.charging); // true | false
console.log(battery.chargingTime); // seconds until full
console.log(battery.dischargingTime); // seconds until emptyReturns
Promise<BatteryManager>interface BatteryManager {
readonly charging: boolean;
readonly chargingTime: number;
readonly dischargingTime: number;
readonly level: number;
}Throws
throw new NotSupportedError // navigator.getBattery unavailableSignature
onChange(listener: (battery: BatteryManager) => void, options?: AddEventListenerOptions): () => voidSubscribes to battery status changes.
Example
const unsubscribe = Native.battery.onChange((battery) => {
console.log(battery.level); // 0.0 – 1.0
console.log(battery.charging); // true | false
});
unsubscribe();Returns
() => void// call to remove the listener
unsubscribe();clipboard
Signature
copy(item: any): Promise<boolean>Copies a value to the clipboard. Accepts string, Element, Selection, object, or array.
Flowchart
flowchart TD
A([clipboard.copy called]) --> B[Convert item to text and html]
B --> C{Clipboard API available?}
C -->|yes| D[copyViaClipboardAPI]
C -->|no| E[copyViaLegacy]
D --> F{Success?}
F -->|yes| G([return true])
F -->|no| E
E --> H{ClipboardItem + write available?}
H -->|yes| I[navigator.clipboard.write with text/html + text/plain]
H -->|no| J{writeText available?}
J -->|yes| K[navigator.clipboard.writeText]
J -->|no| L[copyViaSelection via execCommand]
L --> M{execCommand success?}
M -->|yes| G
M -->|no| N[copyViaClipboardData IE fallback]
N --> O([return boolean])Example
// String
await Native.clipboard.copy('Hello world');
// DOM element (copies outerHTML + textContent)
await Native.clipboard.copy(document.querySelector('.content'));
// Object → serialized as JSON
await Native.clipboard.copy({ key: 'value' });
// Current selection
await Native.clipboard.copy(window.getSelection());Returns
Promise<boolean>Signature
paste(): Promise<string>Reads the current clipboard content as a string.
Flowchart
flowchart TD
A([clipboard.paste called]) --> B{Clipboard API available?}
B -->|yes| C[pasteViaClipboardAPI]
B -->|no| D[pasteViaLegacy]
C --> E{ClipboardItem + read available?}
E -->|yes| F[navigator.clipboard.read]
E -->|no| G[navigator.clipboard.readText]
F --> H{text/html type found?}
H -->|yes| I([return html string])
H -->|no| J{text/plain found?}
J -->|yes| K([return plain string])
J -->|no| D
G --> L{Success?}
L -->|yes| M([return string])
L -->|no| D
D --> N[pasteViaSelection via execCommand]
N --> O{Success?}
O -->|yes| P([return string])
O -->|no| Q[pasteViaClipboardData IE fallback]
Q --> R([return string])Example
const text = await Native.clipboard.paste();
console.log(text); // HTML string if available, plain text otherwiseReturns
Promise<string>dimension
value · environment · onChange
Signature
get value(): DimensionsReturns current viewport dimensions, device pixel ratio, and orientation.
Example
const { innerWidth, innerHeight, outerWidth, outerHeight, scale, orientation } = Native.dimension.value;
console.log(innerWidth, innerHeight); // visible viewport size
console.log(scale); // device pixel ratio e.g. 2, 3
if (orientation === Orientation.Portrait) {
console.log('Portrait mode');
}Returns
Dimensionsinterface Dimensions {
outerWidth: number;
outerHeight: number;
innerWidth: number;
innerHeight: number;
scale: number;
orientation: Orientation;
}
enum Orientation {
Portrait = 'portrait',
Landscape = 'landscape',
Unknown = 'unknown',
}Signature
environment: EnvironmentProvides access to CSS environment variable values: safe-area-inset, keyboard-inset, titlebar-area, and viewport-segment.
Example
// Safe area insets (e.g. iPhone notch / Dynamic Island)
const inset = Native.dimension.environment.safeAreaInset.value;
console.log(inset.top, inset.bottom, inset.left, inset.right);
// Virtual keyboard height
const kb = Native.dimension.environment.keyboardInset.value;
console.log(kb.height); // 0 when keyboard is hidden
// Subscribe to safe area changes
const unsubscribe = Native.dimension.environment.safeAreaInset.onChange((inset) => {
document.body.style.paddingBottom = inset.bottom + 'px';
});
unsubscribe();Returns
Environmentinterface Environment {
safeAreaInset: EnvironmentPreset<'safe-area-inset'>;
safeAreaMaxInset: EnvironmentPreset<'safe-area-max-inset'>;
keyboardInset: EnvironmentPreset<'keyboard-inset'>;
titlebarArea: EnvironmentPreset<'titlebar-area'>;
viewportSegment: EnvironmentPreset<'viewport-segment'>;
}
interface EnvironmentPreset<K> {
get value(): EnvironmentPresetValues<K>;
onChange(listener: (value: EnvironmentPresetValues<K>) => void, options?: AddEventListenerOptions): () => void;
}Signature
onChange(listener: (dimension: Dimensions) => void, options?: AddEventListenerOptions): () => voidSubscribes to viewport dimension and orientation changes.
Example
const unsubscribe = Native.dimension.onChange((dimension) => {
console.log(dimension.innerWidth, dimension.innerHeight);
console.log(dimension.orientation); // 'portrait' | 'landscape'
});
unsubscribe();Returns
() => void// call to remove the listener
unsubscribe();fullscreen
supported · element · isFullscreen · request · exit · onChange · onError
Signature
get supported(): booleanReturns whether fullscreen is supported in the current environment.
Example
if (Native.fullscreen.supported) {
await Native.fullscreen.request();
}Returns
boolean// true — standard or vendor-prefixed fullscreen API detected,
// or iOS video with webkitSupportsFullscreen
// false — no fullscreen supportSignature
get element(): Element | nullReturns the element currently displayed in fullscreen, or null if not in fullscreen.
Example
const el = Native.fullscreen.element;
if (el !== null) {
console.log(el.tagName); // e.g. 'VIDEO', 'DIV'
}Returns
Element | null// Element — the current fullscreen element
// null — not in fullscreenSignature
get isFullscreen(): booleanReturns whether fullscreen is currently active.
Example
console.log(Native.fullscreen.isFullscreen); // true | falseReturns
booleanSignature
request(target?: Element, options?: FullscreenOptions): Promise<void>Requests fullscreen for an element.
Flowchart
flowchart TD
A([Fullscreen.request called]) --> B{target defined?}
B -->|no| C[getDefaultTarget]
B -->|yes| D{api available?}
C --> D
D -->|yes| E[Call element api.request]
D -->|no| F[fallbackToIOSVideo]
E --> G{Promise returned?}
G -->|yes| H{Resolved?}
H -->|yes| I([resolve])
H -->|no| F
G -->|no| I
F --> J{iOS + VIDEO + webkitSupportsFullscreen?}
J -->|yes| K[video.webkitEnterFullscreen]
K --> I
J -->|no| L([Throw NotSupportedError])Example
// Default: documentElement on desktop, first video on iOS
await Native.fullscreen.request();
// Specific element
await Native.fullscreen.request(document.getElementById('player'));
// With options
await Native.fullscreen.request(element, { navigationUI: 'hide' });Returns
Promise<void>Throws
throw new NotSupportedError // element does not support fullscreenthrow new NotSupportedError // iOS video lacks webkitEnterFullscreenthrow new InvalidStateError // iOS video not yet playedSignature
exit(): Promise<void>Exits fullscreen.
Flowchart
flowchart TD
A([Fullscreen.exit called]) --> B{api available?}
B -->|yes| C[Call document api.exit]
B -->|no| D[fallbackToIOSVideo]
C --> E{Promise returned?}
E -->|yes| F{Resolved?}
F -->|yes| G([resolve])
F -->|no| D
E -->|no| G
D --> H{iOS + displaying fullscreen video found?}
H -->|yes| I[webkitExitFullscreen]
I --> G
H -->|no| J{getElement null?}
J -->|yes| G
J -->|no| K([Throw NotSupportedError])Example
await Native.fullscreen.exit();Returns
Promise<void>Throws
throw new NotSupportedError // failed to exit fullscreenSignature
onChange(listener: (payload: FullscreenEventPayload) => void, options?: AddEventListenerOptions): () => voidSubscribes to fullscreen state changes.
Example
const unsubscribe = Native.fullscreen.onChange((payload) => {
console.log(payload.isFullscreen); // true | false
console.log(payload.element); // Element
console.log(payload.nativeEvent); // Event
});
unsubscribe();Returns
() => void// call to remove the listener
unsubscribe();Signature
onError(listener: (payload: FullscreenEventPayload) => void, options?: AddEventListenerOptions): () => voidSubscribes to fullscreen errors.
Example
const unsubscribe = Native.fullscreen.onError((payload) => {
console.log(payload.isFullscreen); // boolean
console.log(payload.element); // Element
console.log(payload.nativeEvent); // Event
});
unsubscribe();Returns
() => void// call to remove the listener
unsubscribe();geolocation
Signature
get supported(): booleanReturns whether geolocation is supported in the current environment.
Example
if (Native.geolocation.supported) {
const coords = await Native.geolocation.value;
console.log(coords.latitude, coords.longitude);
}Returns
boolean// true — geolocation API support,
// false — no geolocation supportSignature
get value(): Promise<GeolocationCoordinates>Returns the current geographic coordinates. Falls back to IP-based location if the Geolocation API is unavailable or permission is denied.
Flowchart
flowchart TD
A([geolocation.value called]) --> B{navigator.geolocation supported?}
B -->|no| F[Fallback to ip-api.com]
B -->|yes| C[Permission.request geolocation]
C --> D{Permission granted?}
D -->|no| F
D -->|yes| E[getCurrentPosition]
E --> G{Success?}
G -->|yes| H([Resolve GeolocationCoordinates])
G -->|no| F
F --> I{ip-api.com responded?}
I -->|yes| J([Resolve approximate coordinates])
I -->|no| K([Throw original error])Example
const coords = await Native.geolocation.value;
console.log(coords.latitude, coords.longitude);
console.log(coords.accuracy); // -1 when resolved via IP fallbackReturns
Promise<GeolocationCoordinates>interface GeolocationCoordinates {
readonly latitude: number;
readonly longitude: number;
readonly accuracy: number; // -1 when resolved via IP fallback
readonly altitude: number | null;
readonly altitudeAccuracy: number | null;
readonly heading: number | null;
readonly speed: number | null;
}Throws
throw new NotSupportedError // geolocation unavailable and IP fallback failedthrow new PermissionNotGrantedError // permission denied and IP fallback failedSignature
onChange(listener: (coordinates: GeolocationCoordinates) => void, options?: AddEventListenerOptions): () => voidSubscribes to geographic position changes.
Example
const unsubscribe = Native.geolocation.onChange((coords) => {
console.log(coords.latitude, coords.longitude);
console.log(coords.accuracy); // meters
});
unsubscribe();Returns
() => void// call to remove the listener
unsubscribe();notification
Signature
get supported(): booleanReturns whether notification is supported in the current environment.
Example
if (Native.notification.supported) {
await Native.notification.send({
title: 'Hello',
body: 'You have a new message.',
icon: '/icon.png',
});
}Returns
boolean// true — notification API support,
// false — no notification supportSignature
send(options: NotificationOptions): Promise<Notification>Sends a native notification after requesting permission.
Example
// Basic notification
const notification = await Native.notification.send({
title: 'Hello',
body: 'You have a new message.',
icon: '/icon.png',
});
// With event handlers
const notification = await Native.notification.send({
title: 'Download complete',
body: 'your-file.zip is ready.',
onClick: () => window.focus(),
onClose: () => console.log('dismissed'),
});
// Close the notification programmatically.
notification.close();Returns
Promise<Notification>interface NotificationOptions {
title: string;
badge?: string;
body?: string;
data?: any;
dir?: NotificationDirection;
icon?: string;
lang?: string;
requireInteraction?: boolean;
silent?: boolean | null;
tag?: string;
onClick?: (event: Event) => any;
onClose?: (event: Event) => any;
onError?: (event: Event) => any;
onShow?: (event: Event) => any;
}Throws
throw new NotSupportedError // window.Notification unavailablethrow new PermissionNotGrantedError // notification permission deniedopen
app · telephone · message · mail · file · directory · setting · camera · contact · share · calendar
Signature
app(options: AppOpenOptions): Promise<AppOpenState>Opens a native app. Falls back through multiple URL strategies until one succeeds.
Flowchart
flowchart TD
A([Native.open.app called]) --> B[Detect current OS]
B --> C[Extract app info by OS]
C --> D[Build URL priority list]
D --> E[tryOpenURL sequentially]
E --> F{Success?}
F -->|yes| G([Resolve AppOpenState])
F -->|no| H{URLs remaining?}
H -->|yes| E
H -->|no| I([Throw URLOpenError])
subgraph tryOpenURL
T1([Register blur and focus events]) --> T2[Open URL via href / iframe / cordova]
T2 --> T3{blur fired?}
T3 -->|yes| T4([resolve])
T3 -->|no| T5([reject via timeout])
end
E -.->|delegates to| T1Example
try {
const result = await Native.open.app({
android: {
scheme: 'ms-excel://',
packageName: 'com.microsoft.office.excel',
allowAppStore: true,
// allowWebStore: false,
// intent: 'intent://#Intent;scheme=ms-excel;...',
// fallback: 'https://www.microsoft.com/ko-kr/microsoft-365/excel',
// timeout: 1000,
},
ios: {
scheme: 'ms-excel://',
trackId: '586683407',
allowAppStore: true,
// allowWebStore: false,
// universal: '',
// bundleId: 'com.microsoft.Office.Excel',
// fallback: 'https://www.microsoft.com/ko-kr/microsoft-365/excel',
// timeout: 2000,
},
windows: {
scheme: 'ms-excel://',
productId: 'cfq7ttc0pr28',
allowAppStore: true,
// allowWebStore: false,
// fallback: 'https://www.microsoft.com/ko-kr/microsoft-365/excel',
// timeout: 750,
},
macos: {
scheme: 'ms-excel://',
trackId: '462058435',
allowAppStore: true,
// allowWebStore: false,
// bundleId: 'com.microsoft.Excel',
// fallback: 'https://www.microsoft.com/ko-kr/microsoft-365/excel',
// timeout: 750,
},
});
switch (result) {
AppOpenState.Intent:
console.log('Opened via Android intent.'); break;
AppOpenState.Universal:
console.log('Opened via Universal Link.'); break;
AppOpenState.Scheme:
console.log('Opened via custom scheme.'); break;
AppOpenState.Fallback:
console.log('Opened via fallback URL.'); break;
AppOpenState.Store:
console.log('Redirected to App Store.'); break;
}
} catch (e) {
if (e instanceof Native.open.Errors.URLOpenError) {
console.error('All URLs exhausted:', e.message);
}
}Returns
Promise<AppOpenState>enum AppOpenState {
Intent = 'Intent',
Universal = 'Universal',
Scheme = 'Scheme',
Fallback = 'Fallback',
Store = 'Store',
}Throws
throw new URLOpenError // all candidate URLs were tried and none succeededSignature
telephone(options: TelephoneOptions): Promise<void>Opens the native phone dialer.
Example
await Native.open.telephone({ to: '+821012345678' });Returns
Promise<void>Throws
throw new URLOpenError // failed to open the phone dialerSignature
message(options: MessageOptions): Promise<void>Opens the native SMS app.
Example
// With pre-filled body
await Native.open.message({
to: '+821012345678',
body: 'Hello from Native.open!',
});Returns
Promise<void>Throws
throw new URLOpenError // failed to open the SMS appSignature
mail(options: MailOptions): Promise<void>Opens the native mail client.
Example
// Single recipient
await Native.open.mail({
to: '[email protected]',
subject: 'Greetings',
body: 'Hi there!',
});
// Multiple recipients with cc / bcc
await Native.open.mail({
to: ['[email protected]', '[email protected]'],
cc: '[email protected]',
bcc: '[email protected]',
subject: 'Greetings',
body: 'Hi there!',
});Returns
Promise<void>Throws
throw new URLOpenError // failed to open the mail clientSignature
file(options?: FileOptions): Promise<File[]>Opens a file picker dialog.
Example
// Single file
const [file] = await Native.open.file({
accept: ['.pdf'],
});
// Multiple files with type filter
const files = await Native.open.file({
multiple: true,
accept: ['.png', '.jpg', 'image/webp'],
startIn: ExplorerStartIn.Pictures,
});Returns
Promise<File[]>Throws
throw new UserCancelledError // user dismissed the pickerthrow new NotSupportedError // showOpenFilePicker and input fallback both unavailableSignature
directory(options?: DirectoryOptions): Promise<FileWithPath[]>Opens a directory picker and returns all files with their relative paths.
Example
// Read-only
const entries = await Native.open.directory();
// Read-write
const entries = await Native.open.directory({
mode: DirectoryExploreMode.ReadWrite,
});
entries.forEach(({ file, relativePath }) => {
console.log(relativePath, file.size); // 'src/index.ts', 1024
});Returns
Promise<FileWithPath[]>interface FileWithPath {
file: File;
relativePath: string;
}Throws
throw new NotSupportedError // showDirectoryPicker and webkitdirectory both unavailablethrow new UserCancelledError // user dismissed the pickerSignature
setting(type: SettingType): Promise<void>Opens a system settings screen. iOS is unsupported.
Example
// General settings
await Native.open.setting(SettingType.General);
// Accessibility settings
await Native.open.setting(SettingType.Accessibility);
// Battery settings (Android 5.1+)
await Native.open.setting(SettingType.Battery);Returns
Promise<void>Throws
throw new URLOpenError // canOpenSetting() returned falsethrow new URLOpenError // all setting URLs failedSignature
camera(options?: CameraOptions): Promise<File[]>Opens the device camera.
Example
// Rear-facing photo (default)
const [photo] = await Native.open.camera();
// Front-facing video
const [video] = await Native.open.camera({
type: CameraType.Video,
capture: CaptureType.User,
});Returns
Promise<File[]>Throws
throw new UserCancelledError // user dismissed the camera UISignature
contact(options?: ContactOptions): Promise<Contact[]>Opens the native contact picker.
Example
// Single contact
const [contact] = await Native.open.contact();
console.log(contact.name, contact.tel);
// Multiple contacts
const contacts = await Native.open.contact({ multiple: true });
contacts.forEach((c) => console.log(c.name, c.email));Returns
Promise<Contact[]>interface Contact {
name?: string;
email?: string;
tel?: string;
address?: string;
icon?: Blob[];
}Throws
throw new NotSupportedError // navigator.contacts unavailableSignature
share(options: ShareData): Promise<void>Opens the native OS share sheet.
Example
// Share a URL
await Native.open.share({
title: 'Check this out',
url: 'https://example.com',
});
// Share text and URL
await Native.open.share({
title: 'Check this out',
text: 'Shared via Native.open',
url: 'https://example.com',
});Returns
Promise<void>Throws
throw new NotSupportedError // navigator.share unavailable or canShare() falsethrow new UserCancelledError // user dismissed the share sheetSignature
calendar(options: CalendarOptions): voidGenerates an RFC 5545 .ics file and triggers a download to open in the default calendar app.
Example
// Basic event
Native.open.calendar({
title: 'Team Sync',
description: 'Weekly alignment meeting',
location: 'Seoul, Korea',
startDate: new Date('2026-04-01T10:00:00Z'),
endDate: new Date('2026-04-01T11:00:00Z'),
});
// Recurring event with alarm
Native.open.calendar({
title: 'Weekly Standup',
startDate: new Date('2026-04-01T09:00:00Z'),
endDate: new Date('2026-04-01T09:15:00Z'),
alarm: [{ minutes: 10, before: true }],
recur: { frequency: 'WEEKLY', byDay: ['MO'], count: 12 },
});
// All-day event
Native.open.calendar({
title: 'Company Holiday',
allDay: true,
startDate: new Date('2026-05-05T00:00:00Z'),
endDate: new Date('2026-05-05T00:00:00Z'),
});Returns
voidpermission
Signature
get supported(): booleanReturns whether permission is supported in the current environment.
Example
if (Native.permission.supported) {
const state = await Native.permission.check(PermissionType.Geolocation);
}Returns
boolean// true — permission API support,
// false — no permission supportSignature
request(type: PermissionType): Promise<PermissionState>Requests a permission. Resolves immediately if already granted.
Flowchart
flowchart TD
A([permission.request called]) --> B[check current state]
B --> C{Already granted?}
C -->|yes| D([Resolve Grant])
C -->|no| E{PermissionType?}
E -->|Notification| F[Notification.requestPermission]
E -->|Geolocation| G[getCurrentPosition to trigger prompt]
E -->|Camera| H[getUserMedia video=true]
E -->|ClipboardRead| I[clipboard.read]
E -->|Microphone| J[getUserMedia audio=true]
E -->|MIDI| k[requestMIDIAccess]
E -->|unknown| L([Resolve Unsupported])
F & G & H & I & J --> M[check state again]
M --> N([Resolve PermissionState])Example
const state = await Native.permission.request(PermissionType.Notification);
switch (state) {
case PermissionState.Grant:
console.log('Permission granted.'); break;
case PermissionState.Denied:
console.log('Permission denied.'); break;
case PermissionState.Prompt:
console.log('Not yet decided.'); break;
case PermissionState.Unsupported:
console.log('Not supported.'); break;
}Returns
Promise<PermissionState>enum PermissionType {
Notification = 'notifications',
Geolocation = 'geolocation',
Camera = 'camera',
ClipboardRead = 'clipboard-read',
Microphone = 'microphone',
MIDI = 'midi',
}
enum PermissionState {
Grant = 'grant',
Denied = 'denied',
Prompt = 'prompt',
Unsupported = 'unsupported',
}Signature
check(type: PermissionType): Promise<PermissionState>Returns the current permission state without triggering a prompt.
Example
// Check before accessing a feature
const state = await Native.permission.check(PermissionType.Geolocation);
if (state === PermissionState.Grant) {
const coords = await Native.geolocation.value;
}Returns
Promise<PermissionState>enum PermissionState {
Grant = 'grant',
Denied = 'denied',
Prompt = 'prompt',
Unsupported = 'unsupported',
}pip
supported · element · isPip · request · exit · onChange · onError
Signature
get supported(): booleanReturns whether Picture-in-Picture is supported in the current environment.
Example
if (Native.pip.supported) {
await Native.pip.request();
}Returns
boolean// true — document.pictureInPictureEnabled is true,
// or a video with webkitSupportsPresentationMode('picture-in-picture') exists
// false — no PiP supportSignature
get element(): HTMLVideoElement | nullReturns the video element currently in Picture-in-Picture, or null if not active.
Example
const el = Native.pip.element;
if (el !== null) {
console.log(el.src); // currently PiP video source
}Returns
HTMLVideoElement | null// HTMLVideoElement — the current PiP video
// null — not in PiPSignature
get isPip(): booleanReturns whether Picture-in-Picture is currently active.
Example
console.log(Native.pip.isPip); // true | falseReturns
booleanSignature
request(target?: HTMLVideoElement): Promise<void>Requests Picture-in-Picture for a video element.
Flowchart
flowchart TD
A([Pip.request called]) --> B{target defined?}
B -->|no| C[getDefaultTarget]
B -->|yes| D{target is video element?}
C --> D
D -->|no| Z([Throw NotSupportedError])
D -->|yes| E{requestPictureInPicture available?}
E -->|yes| F[Call video.requestPictureInPicture]
E -->|no| G[fallbackToWebkit]
F --> H{Promise returned?}
H -->|yes| I{Resolved?}
I -->|yes| J([resolve])
I -->|no| G
H -->|no| J
G --> K{webkitSupportsPresentationMode PIP?}
K -->|yes| L[webkitSetPresentationMode picture-in-picture]
L --> J
K -->|no| M([Throw NotSupportedError])Example
// Default: first video element
await Native.pip.request();
// Specific video element
await Native.pip.request(document.querySelector('video#player'));Returns
Promise<void>Throws
throw new NotSupportedError // target is not a video elementthrow new NotSupportedError // PiP disabled on this element (disablePictureInPicture)throw new NotSupportedError // requestPictureInPicture and webkitSetPresentationMode both unavailablethrow new InvalidStateError // PiP transition already in progressSignature
exit(): Promise<void>Exits Picture-in-Picture.
Flowchart
flowchart TD
A([Pip.exit called]) --> B{exitPictureInPicture available?}
B -->|yes| C[Call document.exitPictureInPicture]
B -->|no| D[fallbackToWebkit]
C --> E{Promise returned?}
E -->|yes| F{Resolved?}
F -->|yes| G([resolve])
F -->|no| D
E -->|no| G
D --> H{webkit PiP video found?}
H -->|yes| I[webkitSetPresentationMode inline]
I --> G
H -->|no| J{any video in PIP mode found?}
J -->|yes| I
J -->|no| K{pictureInPictureElement null?}
K -->|yes| G
K -->|no| L([Throw NotSupportedError])Example
await Native.pip.exit();Returns
Promise<void>Throws
throw new NotSupportedError // failed to exit PiPSignature
onChange(listener: (payload: PipEventPayload) => void, options?: AddEventListenerOptions): () => voidSubscribes to Picture-in-Picture state changes.
Example
const unsubscribe = Native.pip.onChange((payload) => {
console.log(payload.isPip); // true | false
console.log(payload.element); // HTMLVideoElement
console.log(payload.nativeEvent); // Event
});
unsubscribe();Returns
() => void// call to remove the listener
unsubscribe();Signature
onError(listener: (payload: PipEventPayload) => void, options?: AddEventListenerOptions): () => voidSubscribes to Picture-in-Picture errors.
Example
const unsubscribe = Native.pip.onError((payload) => {
console.log(payload.isPip); // boolean
console.log(payload.element); // HTMLVideoElement
console.log(payload.nativeEvent); // Event
});
unsubscribe();Returns
() => void// call to remove the listener
unsubscribe();platform
os · browser · engine · device · locale · gpu · userAgent · ready · isWebview · isNode · isStandalone
Signature
get os(): NameVersionPair<OS>Returns the detected OS name and version.
Example
const { name, version } = Native.platform.os;
switch (name) {
case OS.iOS:
console.log('iOS', version); break;
case OS.Android:
console.log('Android', version); break;
case OS.Windows:
console.log('Windows', version); break;
case OS.MacOS:
console.log('macOS', version); break;
}Returns
NameVersionPair<OS>interface NameVersionPair<T> {
name: T;
version: string;
}
enum OS {
Unknown = 'Unknown',
Android = 'Android',
iOS = 'iOS',
Windows = 'Windows',
MacOS = 'MacOS',
}Signature
get browser(): NameVersionPair<Browsers>Returns the detected browser name and version.
Example
const { name, version } = Native.platform.browser;
if (name === Browsers.Safari) {
console.log('Safari', version); // e.g. '17.0'
}Returns
NameVersionPair<Browsers>enum Browsers {
Unknown = 'Unknown',
Chrome = 'Chrome',
Safari = 'Safari',
Edge = 'Edge',
Firefox = 'Firefox',
Opera = 'Opera',
IE = 'IE',
SamsungInternet = 'SamsungInternet',
}Signature
get engine(): NameVersionPair<Engines>Returns the detected rendering engine name and version.
Example
const { name, version } = Native.platform.engine;
if (name === Engines.Blink) {
console.log('Blink', version); // e.g. '120.0.6099.62'
}Returns
NameVersionPair<Engines>enum Engines {
Unknown = 'Unknown',
EdgeHTML = 'EdgeHTML',
ArkWeb = 'ArkWeb',
Blink = 'Blink',
Presto = 'Presto',
WebKit = 'WebKit',
Trident = 'Trident',
Gecko = 'Gecko',
}Signature
get device(): DevicesReturns the device category: Mobile, Desktop, or Unknown.
Example
if (Native.platform.device === Devices.Mobile) {
console.log('Running on a mobile device');
}Returns
Devicesenum Devices {
Unknown = 'Unknown',
Mobile = 'Mobile',
Desktop = 'Desktop',
}Signature
get locale(): LocaleReturns the current locale, timezone, UTC offset, and text direction.
Example
const { language, languages, timezone, offset, isRTL } = Native.platform.locale;
console.log(language); // 'ko-KR'
console.log(languages); // ['ko-KR', 'en-US']
console.log(timezone); // 'Asia/Seoul'
console.log(offset); // 540 (UTC+9 in minutes)
console.log(isRTL); // falseReturns
Localeinterface Locale {
language: string | null;
languages: string[];
timezone: string | null;
offset: number;
isRTL: boolean;
}Signature
get gpu(): GPUReturns GPU information. Await Native.platform.ready for guaranteed complete data.
Example
// Guaranteed complete data
await Native.platform.ready;
const { vendor, architecture, device, description } = Native.platform.gpu;
console.log(vendor); // 'apple'
console.log(architecture); // 'common-3'
console.log(device); // 'Apple M2'Returns
GPUinterface GPU {
vendor?: string;
architecture?: string;
device?: string;
description?: string;
}Signature
get userAgent(): string
set userAgent(value: string)Gets or sets the User-Agent string used for all platform detection. The setter invalidates all parsed caches.
Example
// Read current UA
console.log(Native.platform.userAgent);
// Override for testing
Native.platform.userAgent = 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X)...';
console.log(Native.platform.os.name); // 'iOS'
// Restore original UA
Native.platform.userAgent = originalUA;
await Native.platform.ready;Returns
stringSignature
get ready(): Promise<void>Resolves when all async platform detection has settled.
Example
await Native.platform.ready;
// All values now reflect high-entropy and WebGPU data
console.log(Native.platform.os.name);
console.log(Native.platform.browser.name);
console.log(Native.platform.gpu.vendor);Returns
Promise<void>Signature
get isWebview(): booleanReturns true when running inside a native WebView.
Example
if (Native.platform.isWebview) {
console.log('Running inside a native WebView');
}Returns
booleanSignature
get isNode(): booleanReturns true when running in a Node.js environment.
Example
if (Native.platform.isNode) {
console.log('Running in Node.js');
}Returns
booleanSignature
get isStandalone(): booleanReturns true when running as an installed PWA.
Example
if (Native.platform.isStandalone) {
console.log('Running as installed PWA');
}Returns
booleantheme
Signature
get value(): string | undefined
set value(color: string | undefined)Gets or sets the browser theme color via the meta theme-color tag.
Example
// Read
console.log(Native.theme.value); // '#ffffff' | undefined
// Set
Native.theme.value = '#1a1a2e';
// Remove
Native.theme.value = undefined;Returns
string | undefinedvibration
Signature
get supported(): booleanReturns whether vibration is supported in the current environment.
Example
if (Native.vibration.supported) {
Native.vibration.run([100, 50, 200]);
}Returns
boolean// true — vibration API support,
// false — no vibration supportSignature
run(pattern: number | number[]): booleanTriggers device vibration. Pass a number for a single pulse or an array to define a pattern.
Example
// Single pulse — 200ms
Native.vibration.run(200);
// Pattern — vibrate 100ms, pause 50ms, vibrate 200ms
Native.vibration.run([100, 50, 200]);
// Stop any ongoing vibration
Native.vibration.run(0);Returns
booleanThrows
throw new NotSupportedError // navigator.vibrate unavailableSignature
stop(): booleanStops any ongoing vibration.
Example
Native.vibration.stop();Returns
boolean// true — stop request accepted by the browser
// false — document is hidden or vibration is unsupportedThrows
throw new NotSupportedError // navigator.vibrate unavailable