@wowlabtech/mini-app-adapter 0.2.0 → 0.2.2

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/dist/index.d.cts CHANGED
@@ -56,6 +56,10 @@ interface MiniAppViewportInsets {
56
56
  safeArea: MiniAppSafeAreaInsets;
57
57
  contentSafeArea: MiniAppSafeAreaInsets;
58
58
  }
59
+ interface MiniAppViewportState {
60
+ height: number;
61
+ stableHeight: number;
62
+ }
59
63
  interface MiniAppInitOptions {
60
64
  /**
61
65
  * Enables verbose logs for platforms that support it.
@@ -170,6 +174,10 @@ interface MiniAppAdapter {
170
174
  * Requests fullscreen mode if supported by the platform.
171
175
  */
172
176
  requestFullscreen?(): void;
177
+ /**
178
+ * Subscribes to viewport size changes. Returns disposer.
179
+ */
180
+ onViewportChange?(callback: (state: MiniAppViewportState) => void): () => void;
173
181
  /**
174
182
  * Returns viewport safe area insets if supported by the platform.
175
183
  */
@@ -216,6 +224,21 @@ interface MiniAppAdapter {
216
224
  * Resolves with true if permission was granted.
217
225
  */
218
226
  requestNotificationsPermission?(): Promise<boolean>;
227
+ /**
228
+ * Prompts user to add the mini app to the home screen if supported.
229
+ * Resolves with true when the prompt succeeded.
230
+ */
231
+ addToHomeScreen?(): Promise<boolean>;
232
+ /**
233
+ * Checks whether the mini app is already added to the home screen, if supported.
234
+ * Returns platform-specific status or 'unknown' when not available.
235
+ */
236
+ checkHomeScreenStatus?(): Promise<'added' | 'not_added' | 'unknown' | string>;
237
+ /**
238
+ * Revokes/denies push notifications if supported by the platform.
239
+ * Resolves with true if notifications were disabled.
240
+ */
241
+ denyNotifications?(): Promise<boolean>;
219
242
  /**
220
243
  * Subscribes to push token updates delivered by native shells.
221
244
  */
@@ -292,6 +315,10 @@ declare abstract class BaseMiniAppAdapter implements MiniAppAdapter {
292
315
  getLaunchParams(): unknown;
293
316
  decodeStartParam(_param: string): unknown;
294
317
  requestFullscreen(): void;
318
+ onViewportChange(callback: (state: {
319
+ height: number;
320
+ stableHeight: number;
321
+ }) => void): () => void;
295
322
  getViewportInsets(): MiniAppViewportInsets | undefined;
296
323
  shareMessage(_message: string): Promise<void>;
297
324
  shareUrl(_url: string, _text: string): void;
@@ -311,6 +338,9 @@ declare abstract class BaseMiniAppAdapter implements MiniAppAdapter {
311
338
  scanQRCode(_options?: MiniAppQrScanOptions): Promise<string | null>;
312
339
  requestPhone(): Promise<string | null>;
313
340
  requestNotificationsPermission(): Promise<boolean>;
341
+ addToHomeScreen(): Promise<boolean>;
342
+ checkHomeScreenStatus(): Promise<'added' | 'not_added' | 'unknown' | string>;
343
+ denyNotifications(): Promise<boolean>;
314
344
  enableVerticalSwipes(): void;
315
345
  disableVerticalSwipes(): void;
316
346
  protected notifyEnvironmentChanged(): void;
@@ -384,6 +414,10 @@ declare class TelegramMiniAppAdapter extends BaseMiniAppAdapter {
384
414
  decodeStartParam(param: string): unknown;
385
415
  requestFullscreen(): void;
386
416
  getViewportInsets(): MiniAppViewportInsets | undefined;
417
+ onViewportChange(callback: (state: {
418
+ height: number;
419
+ stableHeight: number;
420
+ }) => void): () => void;
387
421
  onAppearanceChange(callback: (appearance: 'dark' | 'light' | undefined) => void): () => void;
388
422
  setBackButtonVisibility(visible: boolean): void;
389
423
  enableVerticalSwipes(): void;
@@ -393,6 +427,8 @@ declare class TelegramMiniAppAdapter extends BaseMiniAppAdapter {
393
427
  shareUrl(url: string, text?: string): void;
394
428
  downloadFile(url: string, filename: string): Promise<void>;
395
429
  shareStory(mediaUrl: string, options?: MiniAppShareStoryOptions): Promise<void>;
430
+ addToHomeScreen(): Promise<boolean>;
431
+ checkHomeScreenStatus(): Promise<'added' | 'not_added' | 'unknown' | string>;
396
432
  requestPhone(): Promise<string | null>;
397
433
  private setupAppearanceWatcher;
398
434
  private notifyAppearance;
@@ -400,6 +436,8 @@ declare class TelegramMiniAppAdapter extends BaseMiniAppAdapter {
400
436
  private prepareViewport;
401
437
  private requestFullscreenInternal;
402
438
  private getViewportMountOptions;
439
+ private safeHeightFromSdk;
440
+ private stableHeightFromSdk;
403
441
  private notifyViewHide;
404
442
  private notifyViewRestore;
405
443
  protected onDestroy(): void;
@@ -430,10 +468,14 @@ declare class VKMiniAppAdapter extends BaseMiniAppAdapter {
430
468
  supports(capability: MiniAppCapability): Promise<boolean>;
431
469
  requestPhone(): Promise<string | null>;
432
470
  requestNotificationsPermission(): Promise<boolean>;
471
+ addToHomeScreen(): Promise<boolean>;
472
+ denyNotifications(): Promise<boolean>;
433
473
  scanQRCode(options?: MiniAppQrScanOptions): Promise<string | null>;
434
474
  onViewHide(callback: () => void): () => void;
435
475
  onViewRestore(callback: () => void): () => void;
436
476
  shareStory(mediaUrl: string, _options?: MiniAppShareStoryOptions): Promise<void>;
477
+ shareUrl(url: string, text?: string): void;
478
+ private shareUrlInternal;
437
479
  downloadFile(url: string, filename: string): Promise<void>;
438
480
  trackConversionEvent(event: string, payload?: Record<string, unknown>): void;
439
481
  trackPixelEvent(event: string, payload?: Record<string, unknown>): void;
@@ -461,9 +503,13 @@ declare class VKMiniAppAdapter extends BaseMiniAppAdapter {
461
503
  }
462
504
 
463
505
  declare class WebMiniAppAdapter extends BaseMiniAppAdapter {
506
+ private deferredPrompt;
464
507
  constructor();
465
508
  downloadFile(url: string, filename: string): Promise<void>;
466
509
  scanQRCode(_options?: MiniAppQrScanOptions): Promise<string | null>;
510
+ shareUrl(url: string, text: string): void;
511
+ addToHomeScreen(): Promise<boolean>;
512
+ checkHomeScreenStatus(): Promise<'added' | 'not_added' | 'unknown' | string>;
467
513
  }
468
514
 
469
515
  interface CreateAdapterOptions {
@@ -508,4 +554,4 @@ declare function trackPixelEvent(event: string, payload?: Record<string, unknown
508
554
 
509
555
  declare function getPlatform(): MiniAppPlatform;
510
556
 
511
- export { AdapterProvider, BaseMiniAppAdapter, type CreateAdapterOptions, MaxMiniAppAdapter, type MiniAppAdapter, type MiniAppCapability, type MiniAppEnvironmentInfo, type MiniAppInitOptions, type MiniAppPlatform, type MiniAppPopupOptions, type MiniAppQrScanOptions, ShellMiniAppAdapter, TelegramMiniAppAdapter, VKMiniAppAdapter, WebMiniAppAdapter, configureVkPixel, createAdapter, createShellAPI, detectPlatform, getActiveAdapter, getPlatform, isShell, isShellAndroid, isShellIOS, readShellPlatform, requestShellPushPermission, shell, storeShellToken, trackConversionEvent, trackPixelEvent, useAdapterTheme, useMiniAppAdapter, useSafeArea };
557
+ export { AdapterProvider, BaseMiniAppAdapter, type CreateAdapterOptions, MaxMiniAppAdapter, type MiniAppAdapter, type MiniAppCapability, type MiniAppEnvironmentInfo, type MiniAppInitOptions, type MiniAppPlatform, type MiniAppPopupOptions, type MiniAppQrScanOptions, type MiniAppViewportState, ShellMiniAppAdapter, TelegramMiniAppAdapter, VKMiniAppAdapter, WebMiniAppAdapter, configureVkPixel, createAdapter, createShellAPI, detectPlatform, getActiveAdapter, getPlatform, isShell, isShellAndroid, isShellIOS, readShellPlatform, requestShellPushPermission, shell, storeShellToken, trackConversionEvent, trackPixelEvent, useAdapterTheme, useMiniAppAdapter, useSafeArea };
package/dist/index.d.ts CHANGED
@@ -56,6 +56,10 @@ interface MiniAppViewportInsets {
56
56
  safeArea: MiniAppSafeAreaInsets;
57
57
  contentSafeArea: MiniAppSafeAreaInsets;
58
58
  }
59
+ interface MiniAppViewportState {
60
+ height: number;
61
+ stableHeight: number;
62
+ }
59
63
  interface MiniAppInitOptions {
60
64
  /**
61
65
  * Enables verbose logs for platforms that support it.
@@ -170,6 +174,10 @@ interface MiniAppAdapter {
170
174
  * Requests fullscreen mode if supported by the platform.
171
175
  */
172
176
  requestFullscreen?(): void;
177
+ /**
178
+ * Subscribes to viewport size changes. Returns disposer.
179
+ */
180
+ onViewportChange?(callback: (state: MiniAppViewportState) => void): () => void;
173
181
  /**
174
182
  * Returns viewport safe area insets if supported by the platform.
175
183
  */
@@ -216,6 +224,21 @@ interface MiniAppAdapter {
216
224
  * Resolves with true if permission was granted.
217
225
  */
218
226
  requestNotificationsPermission?(): Promise<boolean>;
227
+ /**
228
+ * Prompts user to add the mini app to the home screen if supported.
229
+ * Resolves with true when the prompt succeeded.
230
+ */
231
+ addToHomeScreen?(): Promise<boolean>;
232
+ /**
233
+ * Checks whether the mini app is already added to the home screen, if supported.
234
+ * Returns platform-specific status or 'unknown' when not available.
235
+ */
236
+ checkHomeScreenStatus?(): Promise<'added' | 'not_added' | 'unknown' | string>;
237
+ /**
238
+ * Revokes/denies push notifications if supported by the platform.
239
+ * Resolves with true if notifications were disabled.
240
+ */
241
+ denyNotifications?(): Promise<boolean>;
219
242
  /**
220
243
  * Subscribes to push token updates delivered by native shells.
221
244
  */
@@ -292,6 +315,10 @@ declare abstract class BaseMiniAppAdapter implements MiniAppAdapter {
292
315
  getLaunchParams(): unknown;
293
316
  decodeStartParam(_param: string): unknown;
294
317
  requestFullscreen(): void;
318
+ onViewportChange(callback: (state: {
319
+ height: number;
320
+ stableHeight: number;
321
+ }) => void): () => void;
295
322
  getViewportInsets(): MiniAppViewportInsets | undefined;
296
323
  shareMessage(_message: string): Promise<void>;
297
324
  shareUrl(_url: string, _text: string): void;
@@ -311,6 +338,9 @@ declare abstract class BaseMiniAppAdapter implements MiniAppAdapter {
311
338
  scanQRCode(_options?: MiniAppQrScanOptions): Promise<string | null>;
312
339
  requestPhone(): Promise<string | null>;
313
340
  requestNotificationsPermission(): Promise<boolean>;
341
+ addToHomeScreen(): Promise<boolean>;
342
+ checkHomeScreenStatus(): Promise<'added' | 'not_added' | 'unknown' | string>;
343
+ denyNotifications(): Promise<boolean>;
314
344
  enableVerticalSwipes(): void;
315
345
  disableVerticalSwipes(): void;
316
346
  protected notifyEnvironmentChanged(): void;
@@ -384,6 +414,10 @@ declare class TelegramMiniAppAdapter extends BaseMiniAppAdapter {
384
414
  decodeStartParam(param: string): unknown;
385
415
  requestFullscreen(): void;
386
416
  getViewportInsets(): MiniAppViewportInsets | undefined;
417
+ onViewportChange(callback: (state: {
418
+ height: number;
419
+ stableHeight: number;
420
+ }) => void): () => void;
387
421
  onAppearanceChange(callback: (appearance: 'dark' | 'light' | undefined) => void): () => void;
388
422
  setBackButtonVisibility(visible: boolean): void;
389
423
  enableVerticalSwipes(): void;
@@ -393,6 +427,8 @@ declare class TelegramMiniAppAdapter extends BaseMiniAppAdapter {
393
427
  shareUrl(url: string, text?: string): void;
394
428
  downloadFile(url: string, filename: string): Promise<void>;
395
429
  shareStory(mediaUrl: string, options?: MiniAppShareStoryOptions): Promise<void>;
430
+ addToHomeScreen(): Promise<boolean>;
431
+ checkHomeScreenStatus(): Promise<'added' | 'not_added' | 'unknown' | string>;
396
432
  requestPhone(): Promise<string | null>;
397
433
  private setupAppearanceWatcher;
398
434
  private notifyAppearance;
@@ -400,6 +436,8 @@ declare class TelegramMiniAppAdapter extends BaseMiniAppAdapter {
400
436
  private prepareViewport;
401
437
  private requestFullscreenInternal;
402
438
  private getViewportMountOptions;
439
+ private safeHeightFromSdk;
440
+ private stableHeightFromSdk;
403
441
  private notifyViewHide;
404
442
  private notifyViewRestore;
405
443
  protected onDestroy(): void;
@@ -430,10 +468,14 @@ declare class VKMiniAppAdapter extends BaseMiniAppAdapter {
430
468
  supports(capability: MiniAppCapability): Promise<boolean>;
431
469
  requestPhone(): Promise<string | null>;
432
470
  requestNotificationsPermission(): Promise<boolean>;
471
+ addToHomeScreen(): Promise<boolean>;
472
+ denyNotifications(): Promise<boolean>;
433
473
  scanQRCode(options?: MiniAppQrScanOptions): Promise<string | null>;
434
474
  onViewHide(callback: () => void): () => void;
435
475
  onViewRestore(callback: () => void): () => void;
436
476
  shareStory(mediaUrl: string, _options?: MiniAppShareStoryOptions): Promise<void>;
477
+ shareUrl(url: string, text?: string): void;
478
+ private shareUrlInternal;
437
479
  downloadFile(url: string, filename: string): Promise<void>;
438
480
  trackConversionEvent(event: string, payload?: Record<string, unknown>): void;
439
481
  trackPixelEvent(event: string, payload?: Record<string, unknown>): void;
@@ -461,9 +503,13 @@ declare class VKMiniAppAdapter extends BaseMiniAppAdapter {
461
503
  }
462
504
 
463
505
  declare class WebMiniAppAdapter extends BaseMiniAppAdapter {
506
+ private deferredPrompt;
464
507
  constructor();
465
508
  downloadFile(url: string, filename: string): Promise<void>;
466
509
  scanQRCode(_options?: MiniAppQrScanOptions): Promise<string | null>;
510
+ shareUrl(url: string, text: string): void;
511
+ addToHomeScreen(): Promise<boolean>;
512
+ checkHomeScreenStatus(): Promise<'added' | 'not_added' | 'unknown' | string>;
467
513
  }
468
514
 
469
515
  interface CreateAdapterOptions {
@@ -508,4 +554,4 @@ declare function trackPixelEvent(event: string, payload?: Record<string, unknown
508
554
 
509
555
  declare function getPlatform(): MiniAppPlatform;
510
556
 
511
- export { AdapterProvider, BaseMiniAppAdapter, type CreateAdapterOptions, MaxMiniAppAdapter, type MiniAppAdapter, type MiniAppCapability, type MiniAppEnvironmentInfo, type MiniAppInitOptions, type MiniAppPlatform, type MiniAppPopupOptions, type MiniAppQrScanOptions, ShellMiniAppAdapter, TelegramMiniAppAdapter, VKMiniAppAdapter, WebMiniAppAdapter, configureVkPixel, createAdapter, createShellAPI, detectPlatform, getActiveAdapter, getPlatform, isShell, isShellAndroid, isShellIOS, readShellPlatform, requestShellPushPermission, shell, storeShellToken, trackConversionEvent, trackPixelEvent, useAdapterTheme, useMiniAppAdapter, useSafeArea };
557
+ export { AdapterProvider, BaseMiniAppAdapter, type CreateAdapterOptions, MaxMiniAppAdapter, type MiniAppAdapter, type MiniAppCapability, type MiniAppEnvironmentInfo, type MiniAppInitOptions, type MiniAppPlatform, type MiniAppPopupOptions, type MiniAppQrScanOptions, type MiniAppViewportState, ShellMiniAppAdapter, TelegramMiniAppAdapter, VKMiniAppAdapter, WebMiniAppAdapter, configureVkPixel, createAdapter, createShellAPI, detectPlatform, getActiveAdapter, getPlatform, isShell, isShellAndroid, isShellIOS, readShellPlatform, requestShellPushPermission, shell, storeShellToken, trackConversionEvent, trackPixelEvent, useAdapterTheme, useMiniAppAdapter, useSafeArea };
package/dist/index.js CHANGED
@@ -607,6 +607,27 @@ var BaseMiniAppAdapter = class {
607
607
  }
608
608
  requestFullscreen() {
609
609
  }
610
+ onViewportChange(callback) {
611
+ if (typeof window === "undefined") {
612
+ return () => {
613
+ };
614
+ }
615
+ const fallbackHeight = () => window.visualViewport?.height ?? window.innerHeight;
616
+ const notify = () => {
617
+ const height = fallbackHeight();
618
+ callback({ height, stableHeight: height });
619
+ };
620
+ notify();
621
+ const onResize = () => notify();
622
+ window.visualViewport?.addEventListener("resize", onResize);
623
+ window.visualViewport?.addEventListener("scroll", onResize);
624
+ window.addEventListener("resize", onResize);
625
+ return () => {
626
+ window.visualViewport?.removeEventListener("resize", onResize);
627
+ window.visualViewport?.removeEventListener("scroll", onResize);
628
+ window.removeEventListener("resize", onResize);
629
+ };
630
+ }
610
631
  getViewportInsets() {
611
632
  return void 0;
612
633
  }
@@ -681,6 +702,25 @@ var BaseMiniAppAdapter = class {
681
702
  return null;
682
703
  }
683
704
  async requestNotificationsPermission() {
705
+ if (typeof Notification === "undefined" || typeof Notification.requestPermission !== "function") {
706
+ return false;
707
+ }
708
+ try {
709
+ const permission = await Notification.requestPermission();
710
+ return permission === "granted";
711
+ } catch (error) {
712
+ console.warn("[tvm-app-adapter] requestNotificationsPermission fallback failed:", error);
713
+ return false;
714
+ }
715
+ }
716
+ async addToHomeScreen() {
717
+ return false;
718
+ }
719
+ async checkHomeScreenStatus() {
720
+ return "unknown";
721
+ }
722
+ async denyNotifications() {
723
+ console.warn("[tvm-app-adapter] denyNotifications fallback is not supported in this environment.");
684
724
  return false;
685
725
  }
686
726
  enableVerticalSwipes() {
@@ -1010,7 +1050,11 @@ import {
1010
1050
  shareURL as shareURLSdk,
1011
1051
  copyTextToClipboard as copyTextToClipboardSdk,
1012
1052
  downloadFile as downloadFileSdk,
1013
- shareStory as shareStorySdk
1053
+ shareStory as shareStorySdk,
1054
+ addToHomeScreen as addToHomeScreenSdk,
1055
+ checkHomeScreenStatus as checkHomeScreenStatusSdk,
1056
+ on,
1057
+ off
1014
1058
  } from "@tma.js/sdk";
1015
1059
 
1016
1060
  // src/lib/features.ts
@@ -1356,6 +1400,63 @@ var TelegramMiniAppAdapter = class extends BaseMiniAppAdapter {
1356
1400
  return void 0;
1357
1401
  }
1358
1402
  }
1403
+ onViewportChange(callback) {
1404
+ const disposers = [];
1405
+ const fallbackHeight = () => typeof window !== "undefined" ? window.visualViewport?.height ?? window.innerHeight : 0;
1406
+ const notify = (state) => {
1407
+ const heightCandidate = state?.height ?? this.safeHeightFromSdk();
1408
+ const stableCandidate = state?.stableHeight ?? this.stableHeightFromSdk();
1409
+ const height = Number.isFinite(heightCandidate) ? heightCandidate : fallbackHeight();
1410
+ const stableHeight = Number.isFinite(stableCandidate) && stableCandidate > 0 ? stableCandidate : height;
1411
+ callback({ height, stableHeight });
1412
+ };
1413
+ const ensureMounted = async () => {
1414
+ try {
1415
+ await ensureViewportMounted(this.getViewportMountOptions());
1416
+ } catch (error) {
1417
+ console.warn("[tvm-app-adapter] ensureViewportMounted failed:", error);
1418
+ }
1419
+ };
1420
+ void ensureMounted().finally(() => notify());
1421
+ const { sdkViewport } = this.getViewportMountOptions();
1422
+ if (typeof sdkViewport.on === "function") {
1423
+ try {
1424
+ const off2 = sdkViewport.on("change", (next) => notify(next));
1425
+ if (typeof off2 === "function") {
1426
+ disposers.push(off2);
1427
+ }
1428
+ } catch (error) {
1429
+ console.warn("[tvm-app-adapter] viewport.on(change) subscription failed:", error);
1430
+ }
1431
+ }
1432
+ try {
1433
+ if (typeof sdkViewport.height?.sub === "function") {
1434
+ disposers.push(sdkViewport.height.sub(() => notify()));
1435
+ }
1436
+ if (typeof sdkViewport.stableHeight?.sub === "function") {
1437
+ disposers.push(sdkViewport.stableHeight.sub(() => notify()));
1438
+ }
1439
+ } catch (error) {
1440
+ console.warn("[tvm-app-adapter] viewport signal subscriptions failed:", error);
1441
+ }
1442
+ if (typeof window !== "undefined") {
1443
+ const onResize = () => notify();
1444
+ window.visualViewport?.addEventListener("resize", onResize);
1445
+ window.addEventListener("resize", onResize);
1446
+ disposers.push(() => {
1447
+ window.visualViewport?.removeEventListener("resize", onResize);
1448
+ window.removeEventListener("resize", onResize);
1449
+ });
1450
+ }
1451
+ return () => {
1452
+ disposers.forEach((dispose) => {
1453
+ try {
1454
+ dispose();
1455
+ } catch {
1456
+ }
1457
+ });
1458
+ };
1459
+ }
1359
1460
  onAppearanceChange(callback) {
1360
1461
  this.appearanceListeners.add(callback);
1361
1462
  callback(this.environment.appearance);
@@ -1433,6 +1534,50 @@ var TelegramMiniAppAdapter = class extends BaseMiniAppAdapter {
1433
1534
  async shareStory(mediaUrl, options) {
1434
1535
  shareStorySdk(mediaUrl, options);
1435
1536
  }
1537
+ async addToHomeScreen() {
1538
+ const isAvailable = typeof addToHomeScreenSdk?.isAvailable === "function" ? addToHomeScreenSdk.isAvailable() : true;
1539
+ if (!isAvailable) {
1540
+ return super.addToHomeScreen();
1541
+ }
1542
+ return new Promise((resolve) => {
1543
+ const cleanup = () => {
1544
+ off("home_screen_added", handleSuccess);
1545
+ off("home_screen_failed", handleFail);
1546
+ };
1547
+ const handleSuccess = () => {
1548
+ cleanup();
1549
+ resolve(true);
1550
+ };
1551
+ const handleFail = () => {
1552
+ cleanup();
1553
+ resolve(false);
1554
+ };
1555
+ on("home_screen_added", handleSuccess);
1556
+ on("home_screen_failed", handleFail);
1557
+ try {
1558
+ addToHomeScreenSdk();
1559
+ } catch (error) {
1560
+ cleanup();
1561
+ console.warn("[tvm-app-adapter] Telegram addToHomeScreen failed:", error);
1562
+ resolve(false);
1563
+ }
1564
+ });
1565
+ }
1566
+ async checkHomeScreenStatus() {
1567
+ try {
1568
+ const status = await checkHomeScreenStatusSdk();
1569
+ if (typeof status === "string") {
1570
+ return status;
1571
+ }
1572
+ if (typeof status === "boolean") {
1573
+ return status ? "added" : "not_added";
1574
+ }
1575
+ return "unknown";
1576
+ } catch (error) {
1577
+ console.warn("[tvm-app-adapter] Telegram checkHomeScreenStatus failed:", error);
1578
+ return "unknown";
1579
+ }
1580
+ }
1436
1581
  async requestPhone() {
1437
1582
  const contactFeature = ensureFeature(requestContact);
1438
1583
  if (!contactFeature.ok) {
@@ -1542,6 +1687,26 @@ var TelegramMiniAppAdapter = class extends BaseMiniAppAdapter {
1542
1687
  }
1543
1688
  };
1544
1689
  }
1690
+ safeHeightFromSdk() {
1691
+ try {
1692
+ if (typeof rawViewport.height === "function") {
1693
+ return rawViewport.height();
1694
+ }
1695
+ } catch {
1696
+ return void 0;
1697
+ }
1698
+ return void 0;
1699
+ }
1700
+ stableHeightFromSdk() {
1701
+ try {
1702
+ if (typeof rawViewport.stableHeight === "function") {
1703
+ return rawViewport.stableHeight();
1704
+ }
1705
+ } catch {
1706
+ return void 0;
1707
+ }
1708
+ return void 0;
1709
+ }
1545
1710
  notifyViewHide() {
1546
1711
  for (const listener of this.viewHideListeners) {
1547
1712
  try {
@@ -1774,6 +1939,39 @@ var VKMiniAppAdapter = class extends BaseMiniAppAdapter {
1774
1939
  return false;
1775
1940
  }
1776
1941
  }
1942
+ async addToHomeScreen() {
1943
+ const supported = await this.supportsBridgeMethod("VKWebAppAddToHomeScreen");
1944
+ if (!supported) {
1945
+ console.warn("[tvm-app-adapter] VK addToHomeScreen not supported");
1946
+ return super.addToHomeScreen();
1947
+ }
1948
+ try {
1949
+ const response = await bridge.send("VKWebAppAddToHomeScreen");
1950
+ if (response && typeof response === "object" && "result" in response) {
1951
+ return Boolean(response.result);
1952
+ }
1953
+ return true;
1954
+ } catch (error) {
1955
+ console.warn("[tvm-app-adapter] VK addToHomeScreen failed:", error);
1956
+ return false;
1957
+ }
1958
+ }
1959
+ async denyNotifications() {
1960
+ const supported = await this.supportsBridgeMethod("VKWebAppDenyNotifications");
1961
+ if (!supported) {
1962
+ return super.denyNotifications();
1963
+ }
1964
+ try {
1965
+ const response = await bridge.send("VKWebAppDenyNotifications");
1966
+ if (response && typeof response === "object" && "result" in response) {
1967
+ return Boolean(response.result);
1968
+ }
1969
+ return true;
1970
+ } catch (error) {
1971
+ console.warn("[tvm-app-adapter] VK deny notifications failed:", error);
1972
+ return false;
1973
+ }
1974
+ }
1777
1975
  async scanQRCode(options) {
1778
1976
  const supportsQrScanner = await this.supportsBridgeMethod("VKWebAppOpenCodeReader");
1779
1977
  if (!supportsQrScanner) {
@@ -1809,6 +2007,25 @@ var VKMiniAppAdapter = class extends BaseMiniAppAdapter {
1809
2007
  };
1810
2008
  await bridge.send("VKWebAppShowStoryBox", bridgeOptions);
1811
2009
  }
2010
+ shareUrl(url, text) {
2011
+ void this.shareUrlInternal(url, text);
2012
+ }
2013
+ async shareUrlInternal(url, text) {
2014
+ const supported = await this.supportsBridgeMethod("VKWebAppShare");
2015
+ if (!supported) {
2016
+ super.shareUrl(url, text ?? "");
2017
+ return;
2018
+ }
2019
+ try {
2020
+ await bridge.send("VKWebAppShare", {
2021
+ link: url,
2022
+ ...text ? { text } : {}
2023
+ });
2024
+ } catch (error) {
2025
+ console.warn("[tvm-app-adapter] VK shareUrl failed:", error);
2026
+ super.shareUrl(url, text ?? "");
2027
+ }
2028
+ }
1812
2029
  async downloadFile(url, filename) {
1813
2030
  const supported = await this.supportsBridgeMethod("VKWebAppDownloadFile");
1814
2031
  if (!supported) {
@@ -2167,6 +2384,13 @@ var WebMiniAppAdapter = class extends BaseMiniAppAdapter {
2167
2384
  languageCode: navigator.language,
2168
2385
  isWebView: false
2169
2386
  });
2387
+ __publicField(this, "deferredPrompt", null);
2388
+ if (typeof window !== "undefined") {
2389
+ window.addEventListener("beforeinstallprompt", (event) => {
2390
+ event.preventDefault();
2391
+ this.deferredPrompt = event;
2392
+ });
2393
+ }
2170
2394
  }
2171
2395
  async downloadFile(url, filename) {
2172
2396
  try {
@@ -2315,6 +2539,49 @@ var WebMiniAppAdapter = class extends BaseMiniAppAdapter {
2315
2539
  }
2316
2540
  });
2317
2541
  }
2542
+ shareUrl(url, text) {
2543
+ if (navigator.share) {
2544
+ try {
2545
+ navigator.share({ title: text, text, url });
2546
+ return;
2547
+ } catch (err) {
2548
+ console.warn("Share cancelled or failed:", err);
2549
+ }
2550
+ }
2551
+ const payload = text ? `${text}
2552
+ ${url}` : url;
2553
+ this.copyTextToClipboard(payload).catch((err) => {
2554
+ console.warn("Share fallback (clipboard) failed:", err);
2555
+ });
2556
+ }
2557
+ async addToHomeScreen() {
2558
+ const isAndroid = /android/i.test(navigator.userAgent);
2559
+ if (!isAndroid || !this.deferredPrompt) {
2560
+ return super.addToHomeScreen();
2561
+ }
2562
+ try {
2563
+ this.deferredPrompt.prompt();
2564
+ const choice = await this.deferredPrompt.userChoice;
2565
+ this.deferredPrompt = null;
2566
+ return choice?.outcome === "accepted";
2567
+ } catch (error) {
2568
+ console.warn("[tvm-app-adapter] Web addToHomeScreen failed:", error);
2569
+ this.deferredPrompt = null;
2570
+ return false;
2571
+ }
2572
+ }
2573
+ async checkHomeScreenStatus() {
2574
+ try {
2575
+ const isStandalone = typeof window !== "undefined" && window.matchMedia?.("(display-mode: standalone)").matches || // iOS Safari specific flag
2576
+ typeof navigator !== "undefined" && navigator.standalone === true;
2577
+ if (isStandalone) {
2578
+ return "added";
2579
+ }
2580
+ return "unknown";
2581
+ } catch {
2582
+ return "unknown";
2583
+ }
2584
+ }
2318
2585
  };
2319
2586
 
2320
2587
  // src/adapters/index.ts