@livx.cc/native-kit 0.19.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@livx.cc/native-kit",
3
- "version": "0.19.0",
3
+ "version": "0.21.0",
4
4
  "description": "Isomorphic native-capabilities kit for PWAs — same API in browser and in an appwrap native shell. Zero dependencies.",
5
5
  "license": "MIT",
6
6
  "author": "Elya Livshitz",
@@ -12,6 +12,7 @@ import { DeviceModule } from '../modules/device';
12
12
  import { GeoModule } from '../modules/geo';
13
13
  import { HapticsModule } from '../modules/haptics';
14
14
  import { HealthModule } from '../modules/health';
15
+ import { KeyboardModule } from '../modules/keyboard';
15
16
  import { LifecycleModule } from '../modules/lifecycle';
16
17
  import { MediaModule } from '../modules/media';
17
18
  import { MotionModule } from '../modules/motion';
@@ -70,6 +71,7 @@ export class NativeKit {
70
71
  public readonly haptics = new HapticsModule(this);
71
72
  public readonly share = new ShareModule(this);
72
73
  public readonly screen = new ScreenModule(this);
74
+ public readonly keyboard = new KeyboardModule(this);
73
75
  public readonly storage = new StorageModule(this);
74
76
  public readonly toast = new ToastModule(this);
75
77
  public readonly ui = new UiModule(this);
@@ -54,6 +54,7 @@ export class WebAdapter implements NativeKitAdapter {
54
54
  photos: 'web',
55
55
  network: 'web',
56
56
  screen: 'wakeLock' in n ? 'web' : 'none',
57
+ keyboard: (window as any).visualViewport ? 'web' : 'none', // VisualViewport resize ≈ keyboard show/hide
57
58
  dialogs: 'web',
58
59
  reviews: 'none',
59
60
  themeColor: 'web', // the browser honors <meta name="theme-color"> itself
@@ -180,6 +181,11 @@ export class WebAdapter implements NativeKitAdapter {
180
181
  case 'screen.orientation.current':
181
182
  return (((screen as any)?.orientation?.type ?? '').startsWith('landscape') ? 'landscape' : 'portrait') as T;
182
183
 
184
+ case 'keyboard.hide':
185
+ // No programmatic IME on web — blurring the focused field dismisses it.
186
+ (document.activeElement as HTMLElement | null)?.blur?.();
187
+ return undefined as T;
188
+
183
189
  case 'reviews.requestReview':
184
190
  throw new KitError('UNSUPPORTED', 'No in-app review prompt on web');
185
191
 
@@ -297,6 +303,9 @@ export class WebAdapter implements NativeKitAdapter {
297
303
  return { picked: true, name: c.name?.[0], phones: c.tel, emails: c.email } as T;
298
304
  }
299
305
 
306
+ case 'contacts.getAll':
307
+ throw new KitError('UNSUPPORTED', 'No bulk contacts read on web — use contacts.pick');
308
+
300
309
  case 'calendar.createEvent':
301
310
  throw new KitError('UNSUPPORTED', 'No calendar access on web');
302
311
 
@@ -368,6 +377,22 @@ export class WebAdapter implements NativeKitAdapter {
368
377
  so.addEventListener('change', fire);
369
378
  return () => so.removeEventListener('change', fire);
370
379
  }
380
+ if (event === 'keyboard.show' || event === 'keyboard.hide') {
381
+ const vv = (window as any).visualViewport;
382
+ if (!vv) return () => {};
383
+ let shown = false;
384
+ const onResize = () => {
385
+ // The keyboard shrinks the visual viewport; the hidden band ≈ its height.
386
+ const height = Math.max(0, window.innerHeight - vv.height - vv.offsetTop);
387
+ const nowShown = height > 120; // threshold filters URL-bar/toolbar resizes
388
+ if (nowShown === shown) return;
389
+ shown = nowShown;
390
+ if (event === 'keyboard.show' && nowShown) cb({ height: Math.round(height) });
391
+ if (event === 'keyboard.hide' && !nowShown) cb(undefined);
392
+ };
393
+ vv.addEventListener('resize', onResize);
394
+ return () => vv.removeEventListener('resize', onResize);
395
+ }
371
396
  if (event === 'app.pause' || event === 'app.resume') {
372
397
  const handler = () => {
373
398
  const paused = document.visibilityState === 'hidden';
package/src/index.ts CHANGED
@@ -22,6 +22,7 @@ export type {
22
22
  export type { ImpactStyle, NotifyType } from './modules/haptics';
23
23
  export type { SharePayload, ShareFile } from './modules/share';
24
24
  export type { Orientation, OrientationLock } from './modules/screen';
25
+ export type { KeyboardInfo } from './modules/keyboard';
25
26
  export type { DeviceInfo } from './modules/device';
26
27
  export type { ScheduleOptions } from './modules/notifications';
27
28
  export type { PushMessage, PushPlatform, PushToken } from './modules/push';
@@ -7,6 +7,12 @@ export interface PickedContact {
7
7
  emails?: string[];
8
8
  }
9
9
 
10
+ export interface Contact {
11
+ name?: string;
12
+ phones?: string[];
13
+ emails?: string[];
14
+ }
15
+
10
16
  /** Contact picker — native: CNContactPicker (no permission needed); web: Contact Picker API where present. */
11
17
  export class ContactsModule {
12
18
  constructor(private kit: NativeKit) {}
@@ -19,4 +25,9 @@ export class ContactsModule {
19
25
  pick(): Promise<PickedContact> {
20
26
  return this.kit.invoke('contacts.pick', undefined, { timeoutMs: 120_000 });
21
27
  }
28
+
29
+ /** Read the full address book; native-only (requires permission), unsupported on web. */
30
+ getAll(): Promise<{ contacts: Contact[] }> {
31
+ return this.kit.invoke('contacts.getAll', undefined, { timeoutMs: 120_000 });
32
+ }
22
33
  }
@@ -0,0 +1,44 @@
1
+ import type { NativeKit } from '../core/NativeKit';
2
+ import type { Unsubscribe } from '../core/types';
3
+
4
+ /** Payload of {@link KeyboardModule.onShow}: the on-screen keyboard's height in CSS pixels,
5
+ * so the page can pad/scroll content above it. */
6
+ export interface KeyboardInfo {
7
+ /** Keyboard height in CSS px (0 when unknown). */
8
+ height: number;
9
+ }
10
+
11
+ /**
12
+ * Software-keyboard control + observation — ONE API across platforms.
13
+ *
14
+ * The classic webview pain: the keyboard slides up and covers the focused input.
15
+ * Native emits `show` with the keyboard height (CSS px) and `hide`, so the page can
16
+ * lift content above it; `hide()` dismisses the keyboard programmatically.
17
+ *
18
+ * Native (iOS `UIKeyboardWillShow/Hide`, Android global-layout/IME insets) reports
19
+ * `capability === 'native'`. Web maps to the VisualViewport API where present
20
+ * (`'web'`), else `'none'`. Branch on {@link capability}, not try/catch.
21
+ */
22
+ export class KeyboardModule {
23
+ constructor(private kit: NativeKit) {}
24
+
25
+ /** 'native' on a shell · 'web' where VisualViewport exists · else 'none'. */
26
+ get capability() {
27
+ return this.kit.capability('keyboard');
28
+ }
29
+
30
+ /** Dismiss the software keyboard (resign first responder / hide the IME). */
31
+ hide(): Promise<void> {
32
+ return this.kit.invoke('keyboard.hide');
33
+ }
34
+
35
+ /** Keyboard shown — payload carries its height in CSS pixels. */
36
+ onShow(cb: (info: KeyboardInfo) => void): Unsubscribe {
37
+ return this.kit.on('keyboard.show', (p) => cb(p as KeyboardInfo));
38
+ }
39
+
40
+ /** Keyboard dismissed. */
41
+ onHide(cb: () => void): Unsubscribe {
42
+ return this.kit.on('keyboard.hide', () => cb());
43
+ }
44
+ }