@wowlabtech/mini-app-adapter 0.2.84 → 0.2.86

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.cjs CHANGED
@@ -1,3286 +1,10 @@
1
- "use strict";
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
9
- var __export = (target, all) => {
10
- for (var name in all)
11
- __defProp(target, name, { get: all[name], enumerable: true });
12
- };
13
- var __copyProps = (to, from, except, desc) => {
14
- if (from && typeof from === "object" || typeof from === "function") {
15
- for (let key of __getOwnPropNames(from))
16
- if (!__hasOwnProp.call(to, key) && key !== except)
17
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
- }
19
- return to;
20
- };
21
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
- // If the importer is in node compatibility mode or this is not an ESM
23
- // file that has been converted to a CommonJS file using a Babel-
24
- // compatible transform (i.e. "__esModule" has not been set), then set
25
- // "default" to the CommonJS "module.exports" for node compatibility.
26
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
- mod
28
- ));
29
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
- var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
1
+ "use strict";var at=Object.create;var z=Object.defineProperty;var st=Object.getOwnPropertyDescriptor;var pt=Object.getOwnPropertyNames;var dt=Object.getPrototypeOf,lt=Object.prototype.hasOwnProperty;var ct=(i,n,e)=>n in i?z(i,n,{enumerable:!0,configurable:!0,writable:!0,value:e}):i[n]=e;var ut=(i,n)=>{for(var e in n)z(i,e,{get:n[e],enumerable:!0})},Ve=(i,n,e,t)=>{if(n&&typeof n=="object"||typeof n=="function")for(let r of pt(n))!lt.call(i,r)&&r!==e&&z(i,r,{get:()=>n[r],enumerable:!(t=st(n,r))||t.enumerable});return i};var se=(i,n,e)=>(e=i!=null?at(dt(i)):{},Ve(n||!i||!i.__esModule?z(e,"default",{value:i,enumerable:!0}):e,i)),ft=i=>Ve(z({},"__esModule",{value:!0}),i);var h=(i,n,e)=>ct(i,typeof n!="symbol"?n+"":n,e);var Ct={};ut(Ct,{AdapterProvider:()=>Ge,BaseMiniAppAdapter:()=>S,ShellMiniAppAdapter:()=>_,TelegramMiniAppAdapter:()=>W,VKMiniAppAdapter:()=>I,WebMiniAppAdapter:()=>F,configureVkPixel:()=>Xe,createAdapter:()=>ze,createShellAPI:()=>j,detectPlatform:()=>$,getActiveAdapter:()=>O,getPlatform:()=>rt,isShell:()=>ve,isShellAndroid:()=>Ie,isShellIOS:()=>We,readShellPlatform:()=>G,requestShellPushPermission:()=>te,shell:()=>Fe,storeShellToken:()=>Oe,trackConversionEvent:()=>et,trackPixelEvent:()=>tt,useAdapterTheme:()=>Ye,useMiniAppAdapter:()=>H,useSafeArea:()=>Ze});module.exports=ft(Ct);var Z=class{constructor(){h(this,"disposers",new Set)}add(n){if(!n)return()=>{};let e=typeof n=="function"?n:typeof n.dispose=="function"?n.dispose.bind(n):void 0;if(!e)return()=>{};let t=!1,r=()=>{if(!t){t=!0;try{e()}catch(o){console.warn("[tvm-app-adapter] disposable failed:",o)}finally{this.disposers.delete(r)}}};return this.disposers.add(r),r}disposeAll(){for(let n of Array.from(this.disposers))try{n()}catch(e){console.warn("[tvm-app-adapter] disposeAll failed:",e)}this.disposers.clear()}};var Ee=typeof window<"u"&&typeof document<"u";function mt(i,n){if(n&&n.trim().length>0)return n;try{let t=new URL(i).pathname.split("/").filter(Boolean).pop();if(t)return t}catch{}return"download"}function xe(i,n,{targetBlank:e}){if(!Ee)return;let t=document.createElement("a");t.href=i,t.download=n,t.rel="noopener noreferrer",e&&(t.target="_blank"),document.body.appendChild(t),t.click(),document.body.removeChild(t)}async function X(i,n,e){if(!i)return;let t=mt(i,n),r=e?.preferBlob??!1;if(!Ee){typeof window<"u"&&window.open(i,"_blank","noopener,noreferrer");return}if(r&&typeof fetch=="function")try{let o=await fetch(i,{credentials:"include"});if(!o.ok)throw new Error(`Failed to download file: ${o.status}`);let p=await o.blob(),d=URL.createObjectURL(p);xe(d,t,{targetBlank:!1}),setTimeout(()=>URL.revokeObjectURL(d),3e4);return}catch(o){console.warn("[tvm-app-adapter] blob download failed, falling back to direct link:",o)}xe(i,t,{targetBlank:!0})}var de=["top","right","bottom","left"],Be={top:0,right:0,bottom:0,left:0};function pe(i){return{...i}}function Q(i,n){if(n)for(let e of de){let t=n[e];typeof t=="number"&&Number.isFinite(t)&&(i[e]+=t)}}function Le(i,n){if(n)for(let e of de){let t=n[e];typeof t=="number"&&Number.isFinite(t)&&(i[e]=Math.max(i[e],t))}}function ht(i,n){return!i||!n?!1:de.every(e=>i[e]===n[e])}function U(i={}){let n=pe(Be);return Q(n,i.environment),i.viewport&&(Q(n,i.viewport.safeArea),Q(n,i.viewport.contentSafeArea)),Q(n,i.additions),Le(n,i.css),Le(n,i.minimum),n}function ee(){if(typeof window>"u"||typeof document>"u")return;let i=getComputedStyle(document.documentElement),n=p=>{let d=parseFloat(i.getPropertyValue(p));return Number.isFinite(d)?d:0},e=n("--safe-area-inset-top"),t=n("--safe-area-inset-right"),r=n("--safe-area-inset-bottom"),o=n("--safe-area-inset-left");if(e||t||r||o)return{top:e,right:t,bottom:r,left:o}}function Te(i){if(!i)return;let n={...Be};return Q(n,i),n}function Re(i){let n=i.windowObj??(typeof window<"u"?window:void 0);if(!n)return;let e=Te(i.getSafeArea());e&&(i.onChange(e),e=pe(e));let t=()=>{let o=Te(i.getSafeArea());o&&(!e||!ht(e,o))&&(i.onChange(o),e=pe(o))};t();let r=i.events??["resize","orientationchange"];for(let o of r)n.addEventListener(o,t);return()=>{for(let o of r)n.removeEventListener(o,t)}}var vt=["shell_ios","shell_android"];var ce=new Set,ue=new Set,fe=new Set,me=new Set,he=null,k=null,yt={platformFlag:"nativePlatform",pushTokenCallback:"nativePushToken",qrResultCallback:"nativeQRResult",deepLinkCallback:"nativeDeepLink",appActiveCallback:"nativeAppActive",appBackgroundCallback:"nativeAppBackground"},_e={...yt},le={};function gt(i){he=i;for(let n of ce)try{n(i)}catch(e){console.warn("[tvm-app-adapter] push token listener failed:",e)}}function ve(i){return vt.includes(i)}function We(i){return i==="shell_ios"}function Ie(i){return i==="shell_android"}function G(){let i=ye();if(!i)return;let n=i[_e.platformFlag];if(n==="shell_ios"||n==="shell_android")return n}function j(i){bt();let n=typeof i=="function"?i:()=>i;return{async openNativeQR(){let e=n();return ve(e)?At():kt()},onPushToken(e){return ce.add(e),he&&queueMicrotask(()=>{try{e(he)}catch(t){console.warn("[tvm-app-adapter] push token listener failed:",t)}}),()=>ce.delete(e)},onDeepLink(e){return ue.add(e),()=>ue.delete(e)},onAppActive(e){return fe.add(e),()=>fe.delete(e)},onAppBackground(e){return me.add(e),()=>me.delete(e)}}}var Fe=j(()=>G()??"web");function Oe(i){return ge({type:"storeToken",payload:i})}function te(){return ge({type:"requestPushPermission"})}function ye(){return typeof window>"u"?null:window}function wt(){let i=ye();if(!i)return null;let n=i.NativeBridge;return!n||typeof n.postMessage!="function"?null:n}function ge(i){let n=wt();if(!n)return!1;try{return n.postMessage(i),!0}catch(e){return console.warn("[tvm-app-adapter] NativeBridge.postMessage failed:",e),!1}}function bt(){let i=ye();if(!i){le={};return}let n=i,e=(t,r)=>{let o=_e[t],p=le[t];p&&p!==o&&delete n[p],le[t]=o,n[o]=r};e("pushTokenCallback",t=>{typeof t=="string"&&(console.log("[tvm-app-adapter] nativePushToken",t),gt(t))}),e("deepLinkCallback",t=>{if(typeof t=="string")for(let r of ue)try{r(t)}catch(o){console.warn("[tvm-app-adapter] deep link listener failed:",o)}}),e("appActiveCallback",()=>{for(let t of fe)try{t()}catch(r){console.warn("[tvm-app-adapter] app active listener failed:",r)}}),e("appBackgroundCallback",()=>{for(let t of me)try{t()}catch(r){console.warn("[tvm-app-adapter] app background listener failed:",r)}}),e("qrResultCallback",t=>{typeof t=="string"&&k&&(clearTimeout(k.timeoutId),k.resolve(t),k=null)})}function At(){return new Promise((i,n)=>{if(k&&(k.reject(new Error("QR request superseded by a new call.")),clearTimeout(k.timeoutId),k=null),!ge({type:"openNativeQR"})){n(new Error("Native bridge is unavailable."));return}let e=setTimeout(()=>{k&&(k.reject(new Error("Native QR request timed out.")),k=null)},6e4);k={resolve:i,reject:n,timeoutId:e}})}async function kt(){if(typeof document>"u")throw new Error("QR scanning requires a browser environment.");let[{Html5Qrcode:i}]=await Promise.all([import("html5-qrcode")]);return new Promise((n,e)=>{let t=`native-shell-qr-${Date.now()}`,r=document.createElement("div");r.style.position="fixed",r.style.inset="0",r.style.background="rgba(0, 0, 0, 0.9)",r.style.display="flex",r.style.flexDirection="column",r.style.alignItems="center",r.style.justifyContent="center",r.style.zIndex="2147483647",r.style.backdropFilter="blur(2px)";let o=document.createElement("div");o.id=t,o.style.width="280px",o.style.height="280px",o.style.borderRadius="16px",o.style.overflow="hidden",o.style.position="relative";let p=document.createElement("button");p.type="button",p.textContent="\u2715",p.style.position="absolute",p.style.top="16px",p.style.right="16px",p.style.background="transparent",p.style.border="none",p.style.color="#fff",p.style.fontSize="28px",p.style.cursor="pointer",r.appendChild(p),r.appendChild(o),document.body.appendChild(r);let d=document.body.style.overflow;document.body.style.overflow="hidden";let l=!1,s=new i(t),u=async(v,m)=>{if(!l){l=!0;try{await s.stop();let A=s.clear;typeof A=="function"&&await Promise.resolve(A.call(s))}catch{}r.remove(),document.body.style.overflow=d,typeof v=="string"?n(v):e(m||new Error("QR scanning was cancelled."))}};p.addEventListener("click",()=>{u(void 0,new Error("QR scanning was cancelled by the user."))}),s.start({facingMode:"environment"},{fps:10,qrbox:{width:240,height:240}},v=>{u(v)},()=>{}).catch(v=>{let m=v instanceof Error?v:new Error("Unable to start HTML5 QR scanner.");u(void 0,m)})})}var S=class{constructor(n,e){h(this,"ready",!1);h(this,"environment");h(this,"listeners",new Set);h(this,"disposables",new Z);h(this,"shell");if(this.shell=j(n),this.environment={platform:n,...e},typeof window<"u"){let t=()=>this.notifyEnvironmentChanged();if(window.addEventListener("resize",t),this.registerDisposable(()=>window.removeEventListener("resize",t)),n!=="web"){let r=this.applyScrollGuards();r&&this.registerDisposable(r)}}}async supports(n){switch(n){case"openExternalLink":return!0;default:return!1}}get platform(){return this.environment.platform}async init(n){this.ready=!0}isReady(){return this.ready}getEnvironment(){return{...this.environment}}destroy(){try{this.onDestroy()}finally{this.disposables.disposeAll(),this.listeners.clear(),this.ready=!1}}subscribe(n){return this.listeners.add(n),()=>{this.listeners.delete(n)}}async setColors(n){if(n.background&&(document.body.style.backgroundColor=n.background),n.header){let e=document.querySelector('meta[name="theme-color"]');e&&e.setAttribute("content",n.header)}}onBackButton(n){let e=()=>n();return window.addEventListener("popstate",e),()=>window.removeEventListener("popstate",e)}onPushToken(n){return this.shell.onPushToken(n)}onDeepLink(n){return this.shell.onDeepLink(n)}async openExternalLink(n){window.open(n,"_blank","noopener,noreferrer")}async openInternalLink(n){window.open(n,"_self","noopener,noreferrer")}async closeApp(){}setBackButtonVisibility(n){}onAppearanceChange(n){return n(this.environment.appearance),()=>{}}getInitData(){}getLaunchParams(){return{customLaunchParams:this.readCustomUrlParams()}}decodeStartParam(n){}requestFullscreen(){}onViewportChange(n){if(typeof window>"u")return()=>{};let e=()=>window.visualViewport?.height??window.innerHeight,t=()=>{let o=e();n({height:o,stableHeight:o})};t();let r=()=>t();return window.visualViewport?.addEventListener("resize",r),window.visualViewport?.addEventListener("scroll",r),window.addEventListener("resize",r),()=>{window.visualViewport?.removeEventListener("resize",r),window.visualViewport?.removeEventListener("scroll",r),window.removeEventListener("resize",r)}}getViewportInsets(){}shareMessage(n){return Promise.resolve()}shareUrl(n,e){}async downloadFile(n,e){await X(n,e)}shareStory(n,e){return Promise.resolve()}trackConversionEvent(n,e){}trackPixelEvent(n,e){}copyTextToClipboard(n){return navigator.clipboard.writeText(n).catch(()=>{let e=document.createElement("textarea");e.value=n,e.style.position="fixed",document.body.appendChild(e),e.focus(),e.select();try{document.execCommand("copy")}catch{}document.body.removeChild(e)})}computeSafeArea(){let n=this.getViewportInsets?.(),e=ee();return U({environment:this.environment.safeArea,viewport:n,css:e})}bindCssVariables(n){}vibrateImpact(n){navigator.vibrate?.(10)}vibrateNotification(n){navigator.vibrate?.([10,30,10])}vibrateSelection(){navigator.vibrate?.(5)}onViewHide(n){return()=>{}}onViewRestore(n){return()=>{}}async showPopup(n){let e=[n.title,n.message].filter(Boolean).join(`
31
2
 
32
- // src/index.ts
33
- var index_exports = {};
34
- __export(index_exports, {
35
- AdapterProvider: () => AdapterProvider,
36
- BaseMiniAppAdapter: () => BaseMiniAppAdapter,
37
- MaxMiniAppAdapter: () => MaxMiniAppAdapter,
38
- ShellMiniAppAdapter: () => ShellMiniAppAdapter,
39
- TelegramMiniAppAdapter: () => TelegramMiniAppAdapter,
40
- VKMiniAppAdapter: () => VKMiniAppAdapter,
41
- WebMiniAppAdapter: () => WebMiniAppAdapter,
42
- configureVkPixel: () => configureVkPixel,
43
- createAdapter: () => createAdapter,
44
- createShellAPI: () => createShellAPI,
45
- detectPlatform: () => detectPlatform,
46
- getActiveAdapter: () => getActiveAdapter,
47
- getPlatform: () => getPlatform,
48
- isShell: () => isShell,
49
- isShellAndroid: () => isShellAndroid,
50
- isShellIOS: () => isShellIOS,
51
- readShellPlatform: () => readShellPlatform,
52
- requestShellPushPermission: () => requestShellPushPermission,
53
- shell: () => shell,
54
- storeShellToken: () => storeShellToken,
55
- trackConversionEvent: () => trackConversionEvent,
56
- trackPixelEvent: () => trackPixelEvent,
57
- useAdapterTheme: () => useAdapterTheme,
58
- useMiniAppAdapter: () => useMiniAppAdapter,
59
- useSafeArea: () => useSafeArea
60
- });
61
- module.exports = __toCommonJS(index_exports);
62
-
63
- // src/lib/disposables.ts
64
- var DisposableBag = class {
65
- constructor() {
66
- __publicField(this, "disposers", /* @__PURE__ */ new Set());
67
- }
68
- add(disposable) {
69
- if (!disposable) {
70
- return () => {
71
- };
72
- }
73
- const dispose = typeof disposable === "function" ? disposable : typeof disposable.dispose === "function" ? disposable.dispose.bind(disposable) : void 0;
74
- if (!dispose) {
75
- return () => {
76
- };
77
- }
78
- let called = false;
79
- const wrapped = () => {
80
- if (called) {
81
- return;
82
- }
83
- called = true;
84
- try {
85
- dispose();
86
- } catch (error) {
87
- console.warn("[tvm-app-adapter] disposable failed:", error);
88
- } finally {
89
- this.disposers.delete(wrapped);
90
- }
91
- };
92
- this.disposers.add(wrapped);
93
- return wrapped;
94
- }
95
- disposeAll() {
96
- for (const disposer of Array.from(this.disposers)) {
97
- try {
98
- disposer();
99
- } catch (error) {
100
- console.warn("[tvm-app-adapter] disposeAll failed:", error);
101
- }
102
- }
103
- this.disposers.clear();
104
- }
105
- };
106
-
107
- // src/lib/download.ts
108
- var isClient = typeof window !== "undefined" && typeof document !== "undefined";
109
- function getFallbackFileName(url, provided) {
110
- if (provided && provided.trim().length > 0) {
111
- return provided;
112
- }
113
- try {
114
- const parsed = new URL(url);
115
- const path = parsed.pathname.split("/").filter(Boolean).pop();
116
- if (path) {
117
- return path;
118
- }
119
- } catch {
120
- }
121
- return "download";
122
- }
123
- function triggerAnchorDownload(href, fileName, { targetBlank }) {
124
- if (!isClient) {
125
- return;
126
- }
127
- const anchor = document.createElement("a");
128
- anchor.href = href;
129
- anchor.download = fileName;
130
- anchor.rel = "noopener noreferrer";
131
- if (targetBlank) {
132
- anchor.target = "_blank";
133
- }
134
- document.body.appendChild(anchor);
135
- anchor.click();
136
- document.body.removeChild(anchor);
137
- }
138
- async function triggerFileDownload(url, fileName, options) {
139
- if (!url) {
140
- return;
141
- }
142
- const safeFileName = getFallbackFileName(url, fileName);
143
- const preferBlob = options?.preferBlob ?? false;
144
- if (!isClient) {
145
- if (typeof window !== "undefined") {
146
- window.open(url, "_blank", "noopener,noreferrer");
147
- }
148
- return;
149
- }
150
- if (preferBlob && typeof fetch === "function") {
151
- try {
152
- const response = await fetch(url, { credentials: "include" });
153
- if (!response.ok) {
154
- throw new Error(`Failed to download file: ${response.status}`);
155
- }
156
- const blob = await response.blob();
157
- const objectUrl = URL.createObjectURL(blob);
158
- triggerAnchorDownload(objectUrl, safeFileName, { targetBlank: false });
159
- setTimeout(() => URL.revokeObjectURL(objectUrl), 3e4);
160
- return;
161
- } catch (error) {
162
- console.warn("[tvm-app-adapter] blob download failed, falling back to direct link:", error);
163
- }
164
- }
165
- triggerAnchorDownload(url, safeFileName, { targetBlank: true });
166
- }
167
-
168
- // src/lib/safeArea.ts
169
- var SAFE_AREA_EDGES = ["top", "right", "bottom", "left"];
170
- var DEFAULT_SAFE_AREA = { top: 0, right: 0, bottom: 0, left: 0 };
171
- function cloneSafeArea(source) {
172
- return { ...source };
173
- }
174
- function addSafeArea(target, source) {
175
- if (!source) {
176
- return;
177
- }
178
- for (const edge of SAFE_AREA_EDGES) {
179
- const value = source[edge];
180
- if (typeof value === "number" && Number.isFinite(value)) {
181
- target[edge] += value;
182
- }
183
- }
184
- }
185
- function applyMinimum(target, source) {
186
- if (!source) {
187
- return;
188
- }
189
- for (const edge of SAFE_AREA_EDGES) {
190
- const value = source[edge];
191
- if (typeof value === "number" && Number.isFinite(value)) {
192
- target[edge] = Math.max(target[edge], value);
193
- }
194
- }
195
- }
196
- function areSafeAreasEqual(a, b) {
197
- if (!a || !b) {
198
- return false;
199
- }
200
- return SAFE_AREA_EDGES.every((edge) => a[edge] === b[edge]);
201
- }
202
- function computeCombinedSafeArea(options = {}) {
203
- const safeArea = cloneSafeArea(DEFAULT_SAFE_AREA);
204
- addSafeArea(safeArea, options.environment);
205
- if (options.viewport) {
206
- addSafeArea(safeArea, options.viewport.safeArea);
207
- addSafeArea(safeArea, options.viewport.contentSafeArea);
208
- }
209
- addSafeArea(safeArea, options.additions);
210
- applyMinimum(safeArea, options.css);
211
- applyMinimum(safeArea, options.minimum);
212
- return safeArea;
213
- }
214
- function readCssSafeArea() {
215
- if (typeof window === "undefined" || typeof document === "undefined") {
216
- return void 0;
217
- }
218
- const styles = getComputedStyle(document.documentElement);
219
- const readEdge = (prop) => {
220
- const value = parseFloat(styles.getPropertyValue(prop));
221
- return Number.isFinite(value) ? value : 0;
222
- };
223
- const top = readEdge("--safe-area-inset-top");
224
- const right = readEdge("--safe-area-inset-right");
225
- const bottom = readEdge("--safe-area-inset-bottom");
226
- const left = readEdge("--safe-area-inset-left");
227
- if (top || right || bottom || left) {
228
- return { top, right, bottom, left };
229
- }
230
- return void 0;
231
- }
232
- function normalizeSafeArea(value) {
233
- if (!value) {
234
- return void 0;
235
- }
236
- const safeArea = { ...DEFAULT_SAFE_AREA };
237
- addSafeArea(safeArea, value);
238
- return safeArea;
239
- }
240
- function createSafeAreaWatcher(options) {
241
- const targetWindow = options.windowObj ?? (typeof window !== "undefined" ? window : void 0);
242
- if (!targetWindow) {
243
- return void 0;
244
- }
245
- let previous = normalizeSafeArea(options.getSafeArea());
246
- if (previous) {
247
- options.onChange(previous);
248
- previous = cloneSafeArea(previous);
249
- }
250
- const handler = () => {
251
- const next = normalizeSafeArea(options.getSafeArea());
252
- if (!next) {
253
- return;
254
- }
255
- if (!previous || !areSafeAreasEqual(previous, next)) {
256
- options.onChange(next);
257
- previous = cloneSafeArea(next);
258
- }
259
- };
260
- handler();
261
- const events = options.events ?? ["resize", "orientationchange"];
262
- for (const eventName of events) {
263
- targetWindow.addEventListener(eventName, handler);
264
- }
265
- return () => {
266
- for (const eventName of events) {
267
- targetWindow.removeEventListener(eventName, handler);
268
- }
269
- };
270
- }
271
-
272
- // src/lib/shell.ts
273
- var SHELL_PLATFORMS = ["shell_ios", "shell_android"];
274
- var SHELL_QR_TIMEOUT_MS = 6e4;
275
- var pushListeners = /* @__PURE__ */ new Set();
276
- var deepLinkListeners = /* @__PURE__ */ new Set();
277
- var activeListeners = /* @__PURE__ */ new Set();
278
- var backgroundListeners = /* @__PURE__ */ new Set();
279
- var lastPushToken = null;
280
- var pendingQrRequest = null;
281
- var DEFAULT_BRIDGE_CONFIG = {
282
- platformFlag: "nativePlatform",
283
- pushTokenCallback: "nativePushToken",
284
- qrResultCallback: "nativeQRResult",
285
- deepLinkCallback: "nativeDeepLink",
286
- appActiveCallback: "nativeAppActive",
287
- appBackgroundCallback: "nativeAppBackground"
288
- };
289
- var bridgeConfig = { ...DEFAULT_BRIDGE_CONFIG };
290
- var installedCallbackNames = {};
291
- function notifyPushListeners(token) {
292
- lastPushToken = token;
293
- for (const listener of pushListeners) {
294
- try {
295
- listener(token);
296
- } catch (error) {
297
- console.warn("[tvm-app-adapter] push token listener failed:", error);
298
- }
299
- }
300
- }
301
- function isShell(platform) {
302
- return SHELL_PLATFORMS.includes(platform);
303
- }
304
- function isShellIOS(platform) {
305
- return platform === "shell_ios";
306
- }
307
- function isShellAndroid(platform) {
308
- return platform === "shell_android";
309
- }
310
- function readShellPlatform() {
311
- const shellWindow = getShellWindow();
312
- if (!shellWindow) {
313
- return void 0;
314
- }
315
- const platform = shellWindow[bridgeConfig.platformFlag];
316
- if (platform === "shell_ios" || platform === "shell_android") {
317
- return platform;
318
- }
319
- return void 0;
320
- }
321
- function createShellAPI(platform) {
322
- installGlobalCallbacks();
323
- const resolvePlatform = typeof platform === "function" ? platform : () => platform;
324
- return {
325
- async openNativeQR() {
326
- const currentPlatform = resolvePlatform();
327
- if (isShell(currentPlatform)) {
328
- return openNativeQrViaBridge();
329
- }
330
- return startHtml5Qrcode();
331
- },
332
- onPushToken(callback) {
333
- pushListeners.add(callback);
334
- if (lastPushToken) {
335
- queueMicrotask(() => {
336
- try {
337
- callback(lastPushToken);
338
- } catch (error) {
339
- console.warn("[tvm-app-adapter] push token listener failed:", error);
340
- }
341
- });
342
- }
343
- return () => pushListeners.delete(callback);
344
- },
345
- onDeepLink(callback) {
346
- deepLinkListeners.add(callback);
347
- return () => deepLinkListeners.delete(callback);
348
- },
349
- onAppActive(callback) {
350
- activeListeners.add(callback);
351
- return () => activeListeners.delete(callback);
352
- },
353
- onAppBackground(callback) {
354
- backgroundListeners.add(callback);
355
- return () => backgroundListeners.delete(callback);
356
- }
357
- };
358
- }
359
- var shell = createShellAPI(() => readShellPlatform() ?? "web");
360
- function storeShellToken(payload) {
361
- return sendBridgeCommand({ type: "storeToken", payload });
362
- }
363
- function requestShellPushPermission() {
364
- return sendBridgeCommand({ type: "requestPushPermission" });
365
- }
366
- function getShellWindow() {
367
- return typeof window === "undefined" ? null : window;
368
- }
369
- function getNativeBridge() {
370
- const shellWindow = getShellWindow();
371
- if (!shellWindow) {
372
- return null;
373
- }
374
- const bridge2 = shellWindow.NativeBridge;
375
- if (!bridge2 || typeof bridge2.postMessage !== "function") {
376
- return null;
377
- }
378
- return bridge2;
379
- }
380
- function sendBridgeCommand(command) {
381
- const bridge2 = getNativeBridge();
382
- if (!bridge2) {
383
- return false;
384
- }
385
- try {
386
- bridge2.postMessage(command);
387
- return true;
388
- } catch (error) {
389
- console.warn("[tvm-app-adapter] NativeBridge.postMessage failed:", error);
390
- return false;
391
- }
392
- }
393
- function installGlobalCallbacks() {
394
- const shellWindow = getShellWindow();
395
- if (!shellWindow) {
396
- installedCallbackNames = {};
397
- return;
398
- }
399
- const target = shellWindow;
400
- const assignCallback = (key, handler) => {
401
- const nextName = bridgeConfig[key];
402
- const previousName = installedCallbackNames[key];
403
- if (previousName && previousName !== nextName) {
404
- delete target[previousName];
405
- }
406
- installedCallbackNames[key] = nextName;
407
- target[nextName] = handler;
408
- };
409
- assignCallback("pushTokenCallback", (token) => {
410
- if (typeof token !== "string") {
411
- return;
412
- }
413
- console.log("[tvm-app-adapter] nativePushToken", token);
414
- notifyPushListeners(token);
415
- });
416
- assignCallback("deepLinkCallback", (path) => {
417
- if (typeof path !== "string") {
418
- return;
419
- }
420
- for (const listener of deepLinkListeners) {
421
- try {
422
- listener(path);
423
- } catch (error) {
424
- console.warn("[tvm-app-adapter] deep link listener failed:", error);
425
- }
426
- }
427
- });
428
- assignCallback("appActiveCallback", () => {
429
- for (const listener of activeListeners) {
430
- try {
431
- listener();
432
- } catch (error) {
433
- console.warn("[tvm-app-adapter] app active listener failed:", error);
434
- }
435
- }
436
- });
437
- assignCallback("appBackgroundCallback", () => {
438
- for (const listener of backgroundListeners) {
439
- try {
440
- listener();
441
- } catch (error) {
442
- console.warn("[tvm-app-adapter] app background listener failed:", error);
443
- }
444
- }
445
- });
446
- assignCallback("qrResultCallback", (value) => {
447
- if (typeof value !== "string") {
448
- return;
449
- }
450
- if (!pendingQrRequest) {
451
- return;
452
- }
453
- clearTimeout(pendingQrRequest.timeoutId);
454
- pendingQrRequest.resolve(value);
455
- pendingQrRequest = null;
456
- });
457
- }
458
- function openNativeQrViaBridge() {
459
- return new Promise((resolve, reject) => {
460
- if (pendingQrRequest) {
461
- pendingQrRequest.reject(new Error("QR request superseded by a new call."));
462
- clearTimeout(pendingQrRequest.timeoutId);
463
- pendingQrRequest = null;
464
- }
465
- if (!sendBridgeCommand({ type: "openNativeQR" })) {
466
- reject(new Error("Native bridge is unavailable."));
467
- return;
468
- }
469
- const timeoutId = setTimeout(() => {
470
- if (pendingQrRequest) {
471
- pendingQrRequest.reject(new Error("Native QR request timed out."));
472
- pendingQrRequest = null;
473
- }
474
- }, SHELL_QR_TIMEOUT_MS);
475
- pendingQrRequest = { resolve, reject, timeoutId };
476
- });
477
- }
478
- async function startHtml5Qrcode() {
479
- if (typeof document === "undefined") {
480
- throw new Error("QR scanning requires a browser environment.");
481
- }
482
- const [{ Html5Qrcode }] = await Promise.all([import("html5-qrcode")]);
483
- return new Promise((resolve, reject) => {
484
- const elementId = `native-shell-qr-${Date.now()}`;
485
- const overlay = document.createElement("div");
486
- overlay.style.position = "fixed";
487
- overlay.style.inset = "0";
488
- overlay.style.background = "rgba(0, 0, 0, 0.9)";
489
- overlay.style.display = "flex";
490
- overlay.style.flexDirection = "column";
491
- overlay.style.alignItems = "center";
492
- overlay.style.justifyContent = "center";
493
- overlay.style.zIndex = "2147483647";
494
- overlay.style.backdropFilter = "blur(2px)";
495
- const reader = document.createElement("div");
496
- reader.id = elementId;
497
- reader.style.width = "280px";
498
- reader.style.height = "280px";
499
- reader.style.borderRadius = "16px";
500
- reader.style.overflow = "hidden";
501
- reader.style.position = "relative";
502
- const closeButton = document.createElement("button");
503
- closeButton.type = "button";
504
- closeButton.textContent = "\u2715";
505
- closeButton.style.position = "absolute";
506
- closeButton.style.top = "16px";
507
- closeButton.style.right = "16px";
508
- closeButton.style.background = "transparent";
509
- closeButton.style.border = "none";
510
- closeButton.style.color = "#fff";
511
- closeButton.style.fontSize = "28px";
512
- closeButton.style.cursor = "pointer";
513
- overlay.appendChild(closeButton);
514
- overlay.appendChild(reader);
515
- document.body.appendChild(overlay);
516
- const previousOverflow = document.body.style.overflow;
517
- document.body.style.overflow = "hidden";
518
- let disposed = false;
519
- const scanner = new Html5Qrcode(elementId);
520
- const cleanup = async (result, error) => {
521
- if (disposed) {
522
- return;
523
- }
524
- disposed = true;
525
- try {
526
- await scanner.stop();
527
- const maybeClear = scanner.clear;
528
- if (typeof maybeClear === "function") {
529
- await Promise.resolve(maybeClear.call(scanner));
530
- }
531
- } catch {
532
- }
533
- overlay.remove();
534
- document.body.style.overflow = previousOverflow;
535
- if (typeof result === "string") {
536
- resolve(result);
537
- } else if (error) {
538
- reject(error);
539
- } else {
540
- reject(new Error("QR scanning was cancelled."));
541
- }
542
- };
543
- closeButton.addEventListener("click", () => {
544
- void cleanup(void 0, new Error("QR scanning was cancelled by the user."));
545
- });
546
- scanner.start(
547
- { facingMode: "environment" },
548
- { fps: 10, qrbox: { width: 240, height: 240 } },
549
- (decodedText) => {
550
- void cleanup(decodedText);
551
- },
552
- () => {
553
- }
554
- ).catch((error) => {
555
- const cause = error instanceof Error ? error : new Error("Unable to start HTML5 QR scanner.");
556
- void cleanup(void 0, cause);
557
- });
558
- });
559
- }
560
-
561
- // src/adapters/baseAdapter.ts
562
- var BaseMiniAppAdapter = class {
563
- constructor(platform, environment) {
564
- __publicField(this, "ready", false);
565
- __publicField(this, "environment");
566
- __publicField(this, "listeners", /* @__PURE__ */ new Set());
567
- __publicField(this, "disposables", new DisposableBag());
568
- __publicField(this, "shell");
569
- this.shell = createShellAPI(platform);
570
- this.environment = {
571
- platform,
572
- ...environment
573
- };
574
- if (typeof window !== "undefined") {
575
- const resizeHandler = () => this.notifyEnvironmentChanged();
576
- window.addEventListener("resize", resizeHandler);
577
- this.registerDisposable(() => window.removeEventListener("resize", resizeHandler));
578
- if (platform !== "web") {
579
- const cleanup = this.applyScrollGuards();
580
- if (cleanup) {
581
- this.registerDisposable(cleanup);
582
- }
583
- }
584
- }
585
- }
586
- supports(capability) {
587
- switch (capability) {
588
- case "openExternalLink":
589
- return true;
590
- default:
591
- return false;
592
- }
593
- }
594
- get platform() {
595
- return this.environment.platform;
596
- }
597
- async init(_options) {
598
- this.ready = true;
599
- }
600
- isReady() {
601
- return this.ready;
602
- }
603
- getEnvironment() {
604
- return { ...this.environment };
605
- }
606
- destroy() {
607
- try {
608
- this.onDestroy();
609
- } finally {
610
- this.disposables.disposeAll();
611
- this.listeners.clear();
612
- this.ready = false;
613
- }
614
- }
615
- subscribe(listener) {
616
- this.listeners.add(listener);
617
- return () => {
618
- this.listeners.delete(listener);
619
- };
620
- }
621
- async setColors(colors) {
622
- if (colors.background) {
623
- document.body.style.backgroundColor = colors.background;
624
- }
625
- if (colors.header) {
626
- const meta = document.querySelector('meta[name="theme-color"]');
627
- if (meta) {
628
- meta.setAttribute("content", colors.header);
629
- }
630
- }
631
- }
632
- onBackButton(callback) {
633
- const handler = () => callback();
634
- window.addEventListener("popstate", handler);
635
- return () => window.removeEventListener("popstate", handler);
636
- }
637
- onPushToken(callback) {
638
- return this.shell.onPushToken(callback);
639
- }
640
- onDeepLink(callback) {
641
- return this.shell.onDeepLink(callback);
642
- }
643
- async openExternalLink(url) {
644
- window.open(url, "_blank", "noopener,noreferrer");
645
- }
646
- async openInternalLink(url) {
647
- window.open(url, "_self", "noopener,noreferrer");
648
- }
649
- async closeApp() {
650
- if (window.history.length > 1) {
651
- window.history.back();
652
- } else {
653
- window.close();
654
- }
655
- }
656
- setBackButtonVisibility(_visible) {
657
- }
658
- onAppearanceChange(callback) {
659
- callback(this.environment.appearance);
660
- return () => {
661
- };
662
- }
663
- getInitData() {
664
- return void 0;
665
- }
666
- getLaunchParams() {
667
- return {
668
- customLaunchParams: this.readCustomUrlParams()
669
- };
670
- }
671
- decodeStartParam(_param) {
672
- return void 0;
673
- }
674
- requestFullscreen() {
675
- }
676
- onViewportChange(callback) {
677
- if (typeof window === "undefined") {
678
- return () => {
679
- };
680
- }
681
- const fallbackHeight = () => window.visualViewport?.height ?? window.innerHeight;
682
- const notify = () => {
683
- const height = fallbackHeight();
684
- callback({ height, stableHeight: height });
685
- };
686
- notify();
687
- const onResize = () => notify();
688
- window.visualViewport?.addEventListener("resize", onResize);
689
- window.visualViewport?.addEventListener("scroll", onResize);
690
- window.addEventListener("resize", onResize);
691
- return () => {
692
- window.visualViewport?.removeEventListener("resize", onResize);
693
- window.visualViewport?.removeEventListener("scroll", onResize);
694
- window.removeEventListener("resize", onResize);
695
- };
696
- }
697
- getViewportInsets() {
698
- return void 0;
699
- }
700
- shareMessage(_message) {
701
- return Promise.resolve();
702
- }
703
- shareUrl(_url, _text) {
704
- }
705
- async downloadFile(url, filename) {
706
- await triggerFileDownload(url, filename);
707
- }
708
- shareStory(_mediaUrl, _options) {
709
- return Promise.resolve();
710
- }
711
- trackConversionEvent(_event, _payload) {
712
- }
713
- trackPixelEvent(_event, _payload) {
714
- }
715
- copyTextToClipboard(text) {
716
- return navigator.clipboard.writeText(text).catch(() => {
717
- const textarea = document.createElement("textarea");
718
- textarea.value = text;
719
- textarea.style.position = "fixed";
720
- document.body.appendChild(textarea);
721
- textarea.focus();
722
- textarea.select();
723
- try {
724
- document.execCommand("copy");
725
- } catch {
726
- }
727
- document.body.removeChild(textarea);
728
- });
729
- }
730
- computeSafeArea() {
731
- const viewportInsets = this.getViewportInsets?.();
732
- const cssSafeArea = readCssSafeArea();
733
- return computeCombinedSafeArea({
734
- environment: this.environment.safeArea,
735
- viewport: viewportInsets,
736
- css: cssSafeArea
737
- });
738
- }
739
- bindCssVariables(_mapper) {
740
- }
741
- vibrateImpact(_style) {
742
- navigator.vibrate?.(10);
743
- }
744
- vibrateNotification(_type) {
745
- navigator.vibrate?.([10, 30, 10]);
746
- }
747
- vibrateSelection() {
748
- navigator.vibrate?.(5);
749
- }
750
- onViewHide(_callback) {
751
- return () => {
752
- };
753
- }
754
- onViewRestore(_callback) {
755
- return () => {
756
- };
757
- }
758
- async showPopup(options) {
759
- const message = [options.title, options.message].filter(Boolean).join("\n\n");
760
- window.alert(message);
761
- const firstButton = options.buttons?.[0];
762
- return firstButton?.id ?? "ok";
763
- }
764
- async scanQRCode(_options) {
765
- return null;
766
- }
767
- async requestPhone() {
768
- return null;
769
- }
770
- async requestNotificationsPermission() {
771
- if (typeof Notification === "undefined" || typeof Notification.requestPermission !== "function") {
772
- return false;
773
- }
774
- try {
775
- const permission = await Notification.requestPermission();
776
- return permission === "granted";
777
- } catch (error) {
778
- console.warn("[tvm-app-adapter] requestNotificationsPermission fallback failed:", error);
779
- return false;
780
- }
781
- }
782
- async addToHomeScreen() {
783
- return false;
784
- }
785
- async checkHomeScreenStatus() {
786
- return "unknown";
787
- }
788
- async denyNotifications() {
789
- console.warn("[tvm-app-adapter] denyNotifications fallback is not supported in this environment.");
790
- return false;
791
- }
792
- enableVerticalSwipes() {
793
- }
794
- disableVerticalSwipes() {
795
- }
796
- notifyEnvironmentChanged() {
797
- for (const listener of this.listeners) {
798
- try {
799
- listener();
800
- } catch (error) {
801
- console.warn("[tvm-app-adapter] environment listener failed:", error);
802
- }
803
- }
804
- }
805
- onDestroy() {
806
- }
807
- registerDisposable(disposable) {
808
- return this.disposables.add(disposable);
809
- }
810
- readCustomUrlParams(isServiceParam) {
811
- if (typeof window === "undefined") {
812
- return {};
813
- }
814
- const result = {};
815
- const append = (source) => {
816
- const keys = /* @__PURE__ */ new Set();
817
- for (const [key] of source.entries()) {
818
- keys.add(key);
819
- }
820
- for (const key of keys) {
821
- if (isServiceParam?.(key)) {
822
- continue;
823
- }
824
- const values = source.getAll(key);
825
- if (!values.length) {
826
- continue;
827
- }
828
- result[key] = values.length === 1 ? values[0] : values;
829
- }
830
- };
831
- append(new URLSearchParams(window.location.search));
832
- const hash = window.location.hash.startsWith("#") ? window.location.hash.slice(1) : window.location.hash;
833
- if (hash && hash.includes("=")) {
834
- append(new URLSearchParams(hash));
835
- }
836
- return result;
837
- }
838
- applyScrollGuards() {
839
- if (typeof document === "undefined") {
840
- return void 0;
841
- }
842
- const html = document.documentElement;
843
- const body = document.body;
844
- if (!html || !body) {
845
- return void 0;
846
- }
847
- const prevHtmlOverscroll = html.style.overscrollBehaviorY;
848
- const prevBodyOverscroll = body.style.overscrollBehaviorY;
849
- const prevBodyTouchAction = body.style.touchAction;
850
- html.style.overscrollBehaviorY = "none";
851
- body.style.overscrollBehaviorY = "none";
852
- body.style.touchAction = "manipulation";
853
- return () => {
854
- html.style.overscrollBehaviorY = prevHtmlOverscroll;
855
- body.style.overscrollBehaviorY = prevBodyOverscroll;
856
- body.style.touchAction = prevBodyTouchAction;
857
- };
858
- }
859
- };
860
-
861
- // src/adapters/maxAdapter.ts
862
- function getMaxBridge() {
863
- return window.WebApp;
864
- }
865
- var MaxMiniAppAdapter = class extends BaseMiniAppAdapter {
866
- constructor() {
867
- super("max");
868
- __publicField(this, "backHandlers", /* @__PURE__ */ new Map());
869
- __publicField(this, "initData");
870
- __publicField(this, "initDataUnsafe");
871
- }
872
- async init(_options) {
873
- if (this.ready) {
874
- return;
875
- }
876
- const bridge2 = getMaxBridge();
877
- bridge2?.ready?.();
878
- this.initData = bridge2?.initData;
879
- this.initDataUnsafe = bridge2?.initDataUnsafe;
880
- const environment = {
881
- platform: "max",
882
- sdkVersion: bridge2?.version,
883
- appVersion: bridge2?.version,
884
- languageCode: bridge2?.initDataUnsafe?.user?.language_code,
885
- isWebView: true
886
- };
887
- this.environment = environment;
888
- this.ready = true;
889
- }
890
- supports(capability) {
891
- const bridge2 = getMaxBridge();
892
- switch (capability) {
893
- case "haptics":
894
- return Boolean(bridge2?.HapticFeedback?.impactOccurred);
895
- case "qrScanner":
896
- return typeof bridge2?.openCodeReader === "function";
897
- case "closeApp":
898
- return typeof bridge2?.close === "function";
899
- case "backButton":
900
- return Boolean(bridge2?.BackButton?.onClick);
901
- case "backButtonVisibility":
902
- return Boolean(bridge2?.BackButton?.show && bridge2.BackButton.hide);
903
- case "openInternalLink":
904
- return typeof bridge2?.openMaxLink === "function";
905
- case "downloadFile":
906
- return typeof bridge2?.downloadFile === "function";
907
- case "requestPhone":
908
- if (!bridge2) {
909
- return false;
910
- }
911
- return typeof bridge2.requestPhoneNumber === "function" || typeof window !== "undefined";
912
- case "popup":
913
- return false;
914
- default:
915
- return false;
916
- }
917
- }
918
- getInitData() {
919
- return this.initData;
920
- }
921
- getLaunchParams() {
922
- return {
923
- launchParams: this.initDataUnsafe,
924
- customLaunchParams: this.readCustomUrlParams()
925
- };
926
- }
927
- onBackButton(callback) {
928
- const bridge2 = getMaxBridge();
929
- if (!bridge2?.BackButton?.onClick) {
930
- return super.onBackButton(callback);
931
- }
932
- const wrapped = () => callback();
933
- const disposer = bridge2.BackButton.onClick(wrapped);
934
- bridge2.BackButton.show?.();
935
- const removeFromBag = this.registerDisposable(() => {
936
- if (typeof disposer === "function") {
937
- disposer();
938
- } else {
939
- bridge2.BackButton?.offClick?.(wrapped);
940
- }
941
- this.backHandlers.delete(callback);
942
- if (!this.backHandlers.size) {
943
- bridge2.BackButton?.hide?.();
944
- }
945
- });
946
- this.backHandlers.set(callback, removeFromBag);
947
- return removeFromBag;
948
- }
949
- setBackButtonVisibility(visible) {
950
- const bridge2 = getMaxBridge();
951
- if (!bridge2?.BackButton) {
952
- return;
953
- }
954
- visible ? bridge2.BackButton.show?.() : bridge2.BackButton.hide?.();
955
- }
956
- async openExternalLink(url) {
957
- const bridge2 = getMaxBridge();
958
- if (bridge2?.openExternalLink) {
959
- bridge2.openExternalLink(url);
960
- return;
961
- }
962
- await super.openExternalLink(url);
963
- }
964
- async openInternalLink(url) {
965
- const bridge2 = getMaxBridge();
966
- if (bridge2?.openMaxLink) {
967
- bridge2.openMaxLink(url);
968
- return;
969
- }
970
- await super.openInternalLink(url);
971
- }
972
- async closeApp() {
973
- const bridge2 = getMaxBridge();
974
- if (bridge2?.close) {
975
- bridge2.close();
976
- return;
977
- }
978
- await super.closeApp();
979
- }
980
- vibrateImpact(style) {
981
- const bridge2 = getMaxBridge();
982
- if (!bridge2?.HapticFeedback?.impactOccurred) {
983
- super.vibrateImpact(style);
984
- return;
985
- }
986
- void bridge2.HapticFeedback.impactOccurred(style).catch((error) => {
987
- console.warn("[mini-app-template] MAX impact haptic failed:", error);
988
- });
989
- }
990
- vibrateNotification(type) {
991
- const bridge2 = getMaxBridge();
992
- if (!bridge2?.HapticFeedback?.notificationOccurred) {
993
- super.vibrateNotification(type);
994
- return;
995
- }
996
- void bridge2.HapticFeedback.notificationOccurred(type).catch((error) => {
997
- console.warn("[mini-app-template] MAX notification haptic failed:", error);
998
- });
999
- }
1000
- vibrateSelection() {
1001
- const bridge2 = getMaxBridge();
1002
- if (!bridge2?.HapticFeedback?.selectionChanged) {
1003
- super.vibrateSelection();
1004
- return;
1005
- }
1006
- void bridge2.HapticFeedback.selectionChanged().catch((error) => {
1007
- console.warn("[mini-app-template] MAX selection haptic failed:", error);
1008
- });
1009
- }
1010
- async scanQRCode(options) {
1011
- const bridge2 = getMaxBridge();
1012
- if (!bridge2?.openCodeReader) {
1013
- return super.scanQRCode(options);
1014
- }
1015
- try {
1016
- const result = await bridge2.openCodeReader(options?.closeOnCapture !== false);
1017
- return result?.value ?? null;
1018
- } catch (error) {
1019
- console.warn("[mini-app-template] MAX QR scanner failed:", error);
1020
- return null;
1021
- }
1022
- }
1023
- async requestPhone() {
1024
- const bridge2 = getMaxBridge();
1025
- if (bridge2?.requestPhoneNumber) {
1026
- try {
1027
- const response = await bridge2.requestPhoneNumber();
1028
- return this.extractPhone(response);
1029
- } catch (error) {
1030
- console.warn("[mini-app-template] MAX requestPhone failed:", error);
1031
- return null;
1032
- }
1033
- }
1034
- return this.requestPhoneViaEvent();
1035
- }
1036
- async showPopup(options) {
1037
- return super.showPopup(options);
1038
- }
1039
- async downloadFile(url, filename) {
1040
- const bridge2 = getMaxBridge();
1041
- if (bridge2?.downloadFile) {
1042
- try {
1043
- await bridge2.downloadFile(url, filename);
1044
- return;
1045
- } catch (error) {
1046
- console.warn("[mini-app-template] MAX downloadFile failed:", error);
1047
- }
1048
- }
1049
- await super.downloadFile(url, filename);
1050
- }
1051
- async requestPhoneViaEvent() {
1052
- if (typeof window === "undefined") {
1053
- return null;
1054
- }
1055
- let providedPromise;
1056
- const detail = {
1057
- providePromise: (promise) => {
1058
- providedPromise = promise;
1059
- }
1060
- };
1061
- window.dispatchEvent(new CustomEvent("WebAppRequestPhone", { detail }));
1062
- if (!providedPromise) {
1063
- console.warn("[mini-app-template] MAX requestPhone not handled: native promise missing");
1064
- return null;
1065
- }
1066
- try {
1067
- const result = await providedPromise;
1068
- return this.extractPhone(result);
1069
- } catch (error) {
1070
- console.warn("[mini-app-template] MAX requestPhone promise rejected:", error);
1071
- return null;
1072
- }
1073
- }
1074
- extractPhone(data) {
1075
- if (typeof data === "string") {
1076
- return data || null;
1077
- }
1078
- if (!data || typeof data !== "object") {
1079
- return null;
1080
- }
1081
- const directPhone = data.phone ?? data.phone_number ?? data.phoneNumber;
1082
- if (typeof directPhone === "string" && directPhone) {
1083
- return directPhone;
1084
- }
1085
- const contact = data.contact;
1086
- if (contact && typeof contact === "object") {
1087
- const nested = contact.phone ?? contact.phone_number ?? contact.phoneNumber;
1088
- if (typeof nested === "string" && nested) {
1089
- return nested;
1090
- }
1091
- }
1092
- return null;
1093
- }
1094
- onDestroy() {
1095
- this.backHandlers.clear();
1096
- super.onDestroy();
1097
- }
1098
- };
1099
-
1100
- // src/adapters/shellAdapter.ts
1101
- var ShellMiniAppAdapter = class extends BaseMiniAppAdapter {
1102
- constructor(platform) {
1103
- super(platform, {
1104
- isWebView: true,
1105
- hasNativeQR: true,
1106
- hasPush: true,
1107
- hasWidgets: true
1108
- });
1109
- }
1110
- supports(capability) {
1111
- switch (capability) {
1112
- case "qrScanner":
1113
- return true;
1114
- case "notifications":
1115
- return true;
1116
- default:
1117
- return super.supports(capability);
1118
- }
1119
- }
1120
- async scanQRCode() {
1121
- try {
1122
- const value = await this.shell.openNativeQR();
1123
- return value ?? null;
1124
- } catch (error) {
1125
- console.warn("[tvm-app-adapter] shell.openNativeQR failed:", error);
1126
- return null;
1127
- }
1128
- }
1129
- async requestNotificationsPermission() {
1130
- return requestShellPushPermission();
1131
- }
1132
- };
1133
-
1134
- // src/adapters/telegramAdapter.ts
1135
- var import_sdk_react = require("@tma.js/sdk-react");
1136
- var import_sdk = require("@tma.js/sdk");
1137
-
1138
- // src/lib/features.ts
1139
- function isFeatureAvailable(feature) {
1140
- if (typeof feature !== "function") {
1141
- return false;
1142
- }
1143
- const candidate = feature;
1144
- if (typeof candidate.isAvailable === "function") {
1145
- try {
1146
- return candidate.isAvailable();
1147
- } catch (error) {
1148
- console.warn("[tvm-app-adapter] feature availability check failed:", error);
1149
- return false;
1150
- }
1151
- }
1152
- return true;
1153
- }
1154
- function ensureFeature(feature, ...args) {
1155
- if (!isFeatureAvailable(feature)) {
1156
- return { ok: false };
1157
- }
1158
- try {
1159
- const value = feature(...args);
1160
- return { ok: true, value };
1161
- } catch (error) {
1162
- console.warn("[tvm-app-adapter] feature call failed:", error);
1163
- return { ok: false };
1164
- }
1165
- }
1166
-
1167
- // src/lib/viewport.ts
1168
- async function ensureViewportMounted(options) {
1169
- const { sdkViewport, fallbackMount } = options;
1170
- if (sdkViewport?.isSupported?.()) {
1171
- if (typeof sdkViewport.isMounted === "function" && sdkViewport.isMounted()) {
1172
- return;
1173
- }
1174
- if (typeof sdkViewport.mount === "function") {
1175
- await sdkViewport.mount();
1176
- }
1177
- return;
1178
- }
1179
- if (typeof fallbackMount === "function") {
1180
- await fallbackMount();
1181
- }
1182
- }
1183
- async function bindViewportCssVars(options) {
1184
- await ensureViewportMounted(options);
1185
- if (typeof options.bindCssVars !== "function") {
1186
- return;
1187
- }
1188
- try {
1189
- options.bindCssVars(options.mapper);
1190
- } catch (error) {
1191
- if (error instanceof Error && /css variables are already bound/i.test(error.message)) {
1192
- return;
1193
- }
1194
- throw error;
1195
- }
1196
- }
1197
-
1198
- // src/adapters/telegramAdapter.ts
1199
- var TelegramMiniAppAdapter = class extends BaseMiniAppAdapter {
1200
- constructor() {
1201
- super("telegram");
1202
- __publicField(this, "backHandlers", /* @__PURE__ */ new Map());
1203
- __publicField(this, "cssVariablesBound", false);
1204
- __publicField(this, "appearanceListeners", /* @__PURE__ */ new Set());
1205
- __publicField(this, "appearanceWatcherDispose");
1206
- __publicField(this, "viewHideListeners", /* @__PURE__ */ new Set());
1207
- __publicField(this, "viewRestoreListeners", /* @__PURE__ */ new Set());
1208
- __publicField(this, "activeWatcherDispose");
1209
- }
1210
- async init(options) {
1211
- if (this.ready) {
1212
- return;
1213
- }
1214
- const debug = Boolean(options?.debug);
1215
- const eruda = Boolean(options?.eruda);
1216
- const mockForMacOS = Boolean(options?.mockForMacOS);
1217
- (0, import_sdk_react.setDebug)(debug);
1218
- (0, import_sdk_react.init)();
1219
- if (!import_sdk_react.miniApp.isSupported()) {
1220
- console.warn("[tvm-app-adapter] miniApp feature is not supported; falling back to limited mode.");
1221
- }
1222
- if (eruda && typeof window !== "undefined" && window.eruda) {
1223
- window.eruda.init();
1224
- window.eruda.position({ x: window.innerWidth - 150, y: window.innerHeight - 150 });
1225
- }
1226
- if (mockForMacOS) {
1227
- let firstThemeSent = false;
1228
- (0, import_sdk_react.mockTelegramEnv)({
1229
- onEvent(event, next) {
1230
- if (event.name === "web_app_request_theme") {
1231
- let tp = {};
1232
- if (firstThemeSent) {
1233
- tp = import_sdk_react.themeParams.state();
1234
- } else {
1235
- firstThemeSent = true;
1236
- tp || (tp = (0, import_sdk_react.retrieveLaunchParams)().tgWebAppThemeParams);
1237
- }
1238
- return (0, import_sdk_react.emitEvent)("theme_changed", { theme_params: tp });
1239
- }
1240
- if (event.name === "web_app_request_safe_area") {
1241
- return (0, import_sdk_react.emitEvent)("safe_area_changed", { left: 0, top: 0, right: 0, bottom: 0 });
1242
- }
1243
- next();
1244
- }
1245
- });
1246
- }
1247
- import_sdk_react.initData.restore();
1248
- import_sdk_react.miniApp.ready();
1249
- const launchParams = (0, import_sdk_react.retrieveLaunchParams)();
1250
- let appearance;
1251
- try {
1252
- appearance = import_sdk_react.miniApp.isDark() ? "dark" : "light";
1253
- } catch {
1254
- appearance = void 0;
1255
- }
1256
- const environment = {
1257
- platform: "telegram",
1258
- sdkVersion: launchParams.tgWebAppVersion,
1259
- languageCode: import_sdk_react.initData.user()?.language_code,
1260
- appearance,
1261
- isWebView: true
1262
- };
1263
- this.environment = environment;
1264
- this.notifyAppearance(environment.appearance);
1265
- import_sdk_react.backButton.mount.ifAvailable();
1266
- if (import_sdk_react.miniApp.mount.isAvailable()) {
1267
- import_sdk_react.themeParams.mount();
1268
- import_sdk_react.miniApp.mount();
1269
- this.bindCssVariables();
1270
- }
1271
- await this.prepareViewport();
1272
- this.setupAppearanceWatcher();
1273
- this.setupActiveWatcher();
1274
- this.ready = true;
1275
- }
1276
- async setColors(colors) {
1277
- const fallback = {};
1278
- if (colors.header) {
1279
- if (import_sdk_react.miniApp.setHeaderColor.isAvailable()) {
1280
- const headerColor = import_sdk_react.miniApp.setHeaderColor.supports?.("rgb") ? colors.header : "bg_color";
1281
- const { ok } = ensureFeature(import_sdk_react.miniApp.setHeaderColor, headerColor);
1282
- if (!ok) {
1283
- fallback.header = colors.header;
1284
- }
1285
- } else {
1286
- fallback.header = colors.header;
1287
- }
1288
- }
1289
- if (colors.background) {
1290
- if (import_sdk_react.miniApp.setBgColor.isAvailable()) {
1291
- const { ok } = ensureFeature(import_sdk_react.miniApp.setBgColor, colors.background);
1292
- if (!ok) {
1293
- fallback.background = colors.background;
1294
- }
1295
- } else {
1296
- fallback.background = colors.background;
1297
- }
1298
- }
1299
- if (colors.footer) {
1300
- if (import_sdk_react.miniApp.setBgColor.isAvailable()) {
1301
- const { ok } = ensureFeature(import_sdk_react.miniApp.setBottomBarColorFp, colors.footer);
1302
- if (!ok) {
1303
- fallback.footer = colors.footer;
1304
- }
1305
- } else {
1306
- fallback.footer = colors.footer;
1307
- }
1308
- }
1309
- if (fallback.header || fallback.background) {
1310
- await super.setColors(fallback);
1311
- }
1312
- }
1313
- copyTextToClipboard(text) {
1314
- return (0, import_sdk.copyTextToClipboard)(text);
1315
- }
1316
- onBackButton(callback) {
1317
- if (!import_sdk_react.backButton.isSupported()) {
1318
- return super.onBackButton(callback);
1319
- }
1320
- const dispose = import_sdk_react.backButton.onClick(() => callback());
1321
- const removeFromBag = this.registerDisposable(() => {
1322
- if (typeof dispose === "function") {
1323
- dispose();
1324
- }
1325
- this.backHandlers.delete(callback);
1326
- if (!this.backHandlers.size) {
1327
- import_sdk_react.backButton.hide();
1328
- }
1329
- });
1330
- this.backHandlers.set(callback, removeFromBag);
1331
- return removeFromBag;
1332
- }
1333
- async openExternalLink(url) {
1334
- try {
1335
- (0, import_sdk_react.openLink)(url, { tryInstantView: true });
1336
- return;
1337
- } catch {
1338
- }
1339
- await super.openExternalLink(url);
1340
- }
1341
- async openInternalLink(url) {
1342
- (0, import_sdk_react.postEvent)("web_app_open_tg_link", { path_full: url });
1343
- }
1344
- enableDebug(state) {
1345
- try {
1346
- state ? import_sdk.closingBehavior.enableConfirmation() : import_sdk.closingBehavior.disableConfirmation();
1347
- } catch {
1348
- }
1349
- }
1350
- supports(capability) {
1351
- switch (capability) {
1352
- case "haptics":
1353
- return isFeatureAvailable(import_sdk_react.hapticFeedback.selectionChanged);
1354
- case "popup":
1355
- return isFeatureAvailable(import_sdk_react.popup.show);
1356
- case "qrScanner":
1357
- return isFeatureAvailable(import_sdk_react.qrScanner.open);
1358
- case "closeApp":
1359
- return isFeatureAvailable(import_sdk_react.miniApp.close);
1360
- case "backButton":
1361
- return import_sdk_react.backButton.isSupported();
1362
- case "backButtonVisibility":
1363
- return import_sdk_react.backButton.hide.isSupported();
1364
- case "bindCssVariables":
1365
- return true;
1366
- case "openExternalLink":
1367
- return isFeatureAvailable(import_sdk_react.openLink);
1368
- case "openInternalLink":
1369
- return true;
1370
- case "requestFullscreen":
1371
- return Boolean(
1372
- typeof import_sdk.viewport.requestFullscreen === "function" || import_sdk_react.viewport.requestFullscreen?.isAvailable?.()
1373
- );
1374
- case "verticalSwipes":
1375
- return Boolean(
1376
- import_sdk.swipeBehavior.enableVertical.isAvailable() || import_sdk.swipeBehavior.disableVertical.isAvailable()
1377
- );
1378
- case "viewVisibility":
1379
- return true;
1380
- case "shareUrl":
1381
- return typeof import_sdk.shareURL === "function";
1382
- case "shareStory":
1383
- return typeof import_sdk.shareStory === "function";
1384
- case "copyTextToClipboard":
1385
- return typeof import_sdk.copyTextToClipboard === "function";
1386
- case "downloadFile":
1387
- return typeof import_sdk.downloadFile === "function";
1388
- case "addToHomeScreen":
1389
- return typeof import_sdk.addToHomeScreen?.isAvailable === "function" ? import_sdk.addToHomeScreen.isAvailable() : typeof import_sdk.addToHomeScreen === "function";
1390
- case "checkHomeScreenStatus":
1391
- return typeof import_sdk.checkHomeScreenStatus === "function";
1392
- case "requestPhone": {
1393
- return Boolean(isFeatureAvailable(import_sdk.requestPhoneAccess) || isFeatureAvailable(import_sdk.requestContact));
1394
- }
1395
- default:
1396
- return false;
1397
- }
1398
- }
1399
- bindCssVariables(mapper) {
1400
- if (this.cssVariablesBound) {
1401
- return;
1402
- }
1403
- try {
1404
- import_sdk_react.themeParams.bindCssVars(mapper);
1405
- this.cssVariablesBound = true;
1406
- } catch (error) {
1407
- if (error instanceof Error && /css variables are already bound/i.test(error.message)) {
1408
- this.cssVariablesBound = true;
1409
- return;
1410
- }
1411
- throw error;
1412
- }
1413
- }
1414
- vibrateImpact(style) {
1415
- if (this.supports("haptics")) {
1416
- import_sdk_react.hapticFeedback.impactOccurred(style);
1417
- }
1418
- }
1419
- vibrateNotification(type) {
1420
- if (this.supports("haptics")) {
1421
- import_sdk_react.hapticFeedback.notificationOccurred(type);
1422
- }
1423
- }
1424
- vibrateSelection() {
1425
- if (this.supports("haptics")) {
1426
- import_sdk_react.hapticFeedback.selectionChanged();
1427
- }
1428
- }
1429
- async showPopup(options) {
1430
- const popupResult = ensureFeature(import_sdk_react.popup.show, {
1431
- title: options.title,
1432
- message: options.message,
1433
- buttons: options.buttons?.map((button) => ({
1434
- id: button.id,
1435
- text: button.text ?? button.id,
1436
- type: button.type ?? "default"
1437
- }))
1438
- });
1439
- if (!popupResult.ok) {
1440
- return super.showPopup(options);
1441
- }
1442
- const response = await popupResult.value;
1443
- return response ?? null;
1444
- }
1445
- async scanQRCode(options) {
1446
- let result = null;
1447
- const closeOnCapture = options?.closeOnCapture ?? true;
1448
- const qrScannerResult = ensureFeature(import_sdk_react.qrScanner.open, {
1449
- onCaptured: (qr) => {
1450
- result = qr;
1451
- if (closeOnCapture) {
1452
- void ensureFeature(import_sdk_react.qrScanner.close);
1453
- }
1454
- }
1455
- });
1456
- if (!qrScannerResult.ok) {
1457
- return super.scanQRCode(options);
1458
- }
1459
- await qrScannerResult.value;
1460
- return result;
1461
- }
1462
- async closeApp() {
1463
- const closeResult = ensureFeature(import_sdk_react.miniApp.close);
1464
- if (closeResult.ok) {
1465
- return;
1466
- }
1467
- await super.closeApp();
1468
- }
1469
- getInitData() {
1470
- try {
1471
- return import_sdk_react.initData.raw();
1472
- } catch {
1473
- return void 0;
1474
- }
1475
- }
1476
- getLaunchParams() {
1477
- const customFromUrl = this.readCustomUrlParams((key) => key.toLowerCase().startsWith("tgwebapp"));
1478
- let customFromStartParam = {};
1479
- try {
1480
- const launchParams = (0, import_sdk_react.retrieveLaunchParams)();
1481
- const startParam = launchParams.tgWebAppStartParam;
1482
- if (typeof startParam === "string" && startParam) {
1483
- customFromStartParam = this.normalizeDecodedStartParam(startParam);
1484
- }
1485
- return {
1486
- launchParams,
1487
- customLaunchParams: {
1488
- ...customFromUrl,
1489
- ...customFromStartParam
1490
- }
1491
- };
1492
- } catch {
1493
- return {
1494
- customLaunchParams: {
1495
- ...customFromUrl,
1496
- ...customFromStartParam
1497
- }
1498
- };
1499
- }
1500
- }
1501
- decodeStartParam(param) {
1502
- try {
1503
- return (0, import_sdk.decodeStartParam)(param);
1504
- } catch {
1505
- return void 0;
1506
- }
1507
- }
1508
- requestFullscreen() {
1509
- void this.requestFullscreenInternal();
1510
- }
1511
- getViewportInsets() {
1512
- try {
1513
- const safeArea = import_sdk_react.viewport.safeAreaInsets();
1514
- const contentSafeArea = import_sdk_react.viewport.contentSafeAreaInsets();
1515
- return {
1516
- safeArea,
1517
- contentSafeArea
1518
- };
1519
- } catch {
1520
- return void 0;
1521
- }
1522
- }
1523
- onViewportChange(callback) {
1524
- const disposers = [];
1525
- const fallbackHeight = () => typeof window !== "undefined" ? window.visualViewport?.height ?? window.innerHeight : 0;
1526
- const notify = (state) => {
1527
- const heightCandidate = state?.height ?? this.safeHeightFromSdk();
1528
- const stableCandidate = state?.stableHeight ?? this.stableHeightFromSdk();
1529
- const height = Number.isFinite(heightCandidate) ? heightCandidate : fallbackHeight();
1530
- const stableHeight = Number.isFinite(stableCandidate) && stableCandidate > 0 ? stableCandidate : height;
1531
- callback({ height, stableHeight });
1532
- };
1533
- const ensureMounted = async () => {
1534
- try {
1535
- await ensureViewportMounted(this.getViewportMountOptions());
1536
- } catch (error) {
1537
- console.warn("[tvm-app-adapter] ensureViewportMounted failed:", error);
1538
- }
1539
- };
1540
- void ensureMounted().finally(() => notify());
1541
- const { sdkViewport } = this.getViewportMountOptions();
1542
- if (typeof sdkViewport.on === "function") {
1543
- try {
1544
- const off2 = sdkViewport.on("change", (next) => notify(next));
1545
- if (typeof off2 === "function") {
1546
- disposers.push(off2);
1547
- }
1548
- } catch (error) {
1549
- console.warn("[tvm-app-adapter] viewport.on(change) subscription failed:", error);
1550
- }
1551
- }
1552
- try {
1553
- if (typeof sdkViewport.height?.sub === "function") {
1554
- disposers.push(sdkViewport.height.sub(() => notify()));
1555
- }
1556
- if (typeof sdkViewport.stableHeight?.sub === "function") {
1557
- disposers.push(sdkViewport.stableHeight.sub(() => notify()));
1558
- }
1559
- } catch (error) {
1560
- console.warn("[tvm-app-adapter] viewport signal subscriptions failed:", error);
1561
- }
1562
- if (typeof window !== "undefined") {
1563
- const onResize = () => notify();
1564
- window.visualViewport?.addEventListener("resize", onResize);
1565
- window.addEventListener("resize", onResize);
1566
- disposers.push(() => {
1567
- window.visualViewport?.removeEventListener("resize", onResize);
1568
- window.removeEventListener("resize", onResize);
1569
- });
1570
- }
1571
- return () => {
1572
- disposers.forEach((dispose) => {
1573
- try {
1574
- dispose();
1575
- } catch {
1576
- }
1577
- });
1578
- };
1579
- }
1580
- onAppearanceChange(callback) {
1581
- this.appearanceListeners.add(callback);
1582
- callback(this.environment.appearance);
1583
- return () => {
1584
- this.appearanceListeners.delete(callback);
1585
- };
1586
- }
1587
- setBackButtonVisibility(visible) {
1588
- if (!import_sdk_react.backButton.isSupported()) {
1589
- return;
1590
- }
1591
- if (visible) {
1592
- import_sdk_react.backButton.show();
1593
- } else {
1594
- import_sdk_react.backButton.hide();
1595
- }
1596
- }
1597
- enableVerticalSwipes() {
1598
- try {
1599
- const sdkSwipe = import_sdk.swipeBehavior;
1600
- if (typeof sdkSwipe.isSupported === "function" && sdkSwipe.isSupported()) {
1601
- if (typeof sdkSwipe.isMounted === "function" && !sdkSwipe.isMounted()) {
1602
- sdkSwipe.mount?.();
1603
- }
1604
- sdkSwipe.enableVertical?.();
1605
- } else if (import_sdk.swipeBehavior.enableVertical.isAvailable()) {
1606
- import_sdk.swipeBehavior.enableVertical();
1607
- }
1608
- } catch (error) {
1609
- console.warn("[tvm-app-adapter] enableVerticalSwipes failed:", error);
1610
- }
1611
- }
1612
- disableVerticalSwipes() {
1613
- try {
1614
- const sdkSwipe = import_sdk.swipeBehavior;
1615
- if (typeof sdkSwipe.isSupported === "function" && sdkSwipe.isSupported()) {
1616
- if (typeof sdkSwipe.isMounted === "function" && !sdkSwipe.isMounted()) {
1617
- sdkSwipe.mount?.();
1618
- }
1619
- sdkSwipe.disableVertical?.();
1620
- } else if (import_sdk.swipeBehavior.disableVertical.isAvailable()) {
1621
- import_sdk.swipeBehavior.disableVertical();
1622
- }
1623
- } catch (error) {
1624
- console.warn("[tvm-app-adapter] disableVerticalSwipes failed:", error);
1625
- }
1626
- }
1627
- onViewHide(callback) {
1628
- this.viewHideListeners.add(callback);
1629
- return () => {
1630
- this.viewHideListeners.delete(callback);
1631
- };
1632
- }
1633
- onViewRestore(callback) {
1634
- this.viewRestoreListeners.add(callback);
1635
- return () => {
1636
- this.viewRestoreListeners.delete(callback);
1637
- };
1638
- }
1639
- shareUrl(url, text) {
1640
- return (0, import_sdk.shareURL)(url, text);
1641
- }
1642
- async downloadFile(url, filename) {
1643
- const result = ensureFeature(import_sdk.downloadFile, url, filename);
1644
- if (result.ok) {
1645
- try {
1646
- await result.value;
1647
- return;
1648
- } catch (error) {
1649
- console.warn("[tvm-app-adapter] Telegram downloadFile failed:", error);
1650
- }
1651
- }
1652
- await super.downloadFile(url, filename);
1653
- }
1654
- async shareStory(mediaUrl, options) {
1655
- const text = options?.telegram?.text ?? options?.text;
1656
- const widgetLink = options?.telegram?.widgetLink ?? (options?.link ? {
1657
- url: options.link.url,
1658
- ...options.link.name ? { name: options.link.name } : {}
1659
- } : void 0);
1660
- (0, import_sdk.shareStory)(mediaUrl, {
1661
- ...text ? { text } : {},
1662
- ...widgetLink ? { widgetLink } : {}
1663
- });
1664
- }
1665
- async addToHomeScreen() {
1666
- const isAvailable = typeof import_sdk.addToHomeScreen?.isAvailable === "function" ? import_sdk.addToHomeScreen.isAvailable() : true;
1667
- if (!isAvailable) {
1668
- return super.addToHomeScreen();
1669
- }
1670
- return new Promise((resolve) => {
1671
- const cleanup = () => {
1672
- (0, import_sdk.off)("home_screen_added", handleSuccess);
1673
- (0, import_sdk.off)("home_screen_failed", handleFail);
1674
- };
1675
- const handleSuccess = () => {
1676
- cleanup();
1677
- resolve(true);
1678
- };
1679
- const handleFail = () => {
1680
- cleanup();
1681
- resolve(false);
1682
- };
1683
- (0, import_sdk.on)("home_screen_added", handleSuccess);
1684
- (0, import_sdk.on)("home_screen_failed", handleFail);
1685
- try {
1686
- (0, import_sdk.addToHomeScreen)();
1687
- } catch (error) {
1688
- cleanup();
1689
- console.warn("[tvm-app-adapter] Telegram addToHomeScreen failed:", error);
1690
- resolve(false);
1691
- }
1692
- });
1693
- }
1694
- async checkHomeScreenStatus() {
1695
- try {
1696
- const status = await (0, import_sdk.checkHomeScreenStatus)();
1697
- if (typeof status === "string") {
1698
- return status;
1699
- }
1700
- if (typeof status === "boolean") {
1701
- return status ? "added" : "not_added";
1702
- }
1703
- return "unknown";
1704
- } catch (error) {
1705
- console.warn("[tvm-app-adapter] Telegram checkHomeScreenStatus failed:", error);
1706
- return "unknown";
1707
- }
1708
- }
1709
- async requestPhone() {
1710
- const contactFeature = ensureFeature(import_sdk.requestContact);
1711
- if (!contactFeature.ok) {
1712
- return super.requestPhone();
1713
- }
1714
- if (import_sdk.requestPhoneAccess) {
1715
- const accessFeature = ensureFeature(import_sdk.requestPhoneAccess);
1716
- if (accessFeature.ok) {
1717
- try {
1718
- await accessFeature.value;
1719
- } catch (error) {
1720
- console.warn("[tvm-app-adapter] Telegram requestPhone access failed:", error);
1721
- }
1722
- }
1723
- }
1724
- try {
1725
- const result = await contactFeature.value;
1726
- if (!result || typeof result !== "object") {
1727
- return null;
1728
- }
1729
- const contact = result.contact;
1730
- const phone = contact?.phoneNumber ?? contact?.phone_number ?? contact?.phone ?? result.phoneNumber ?? result.phone_number ?? result.phone;
1731
- return typeof phone === "string" && phone ? phone : null;
1732
- } catch (error) {
1733
- console.warn("[tvm-app-adapter] Telegram requestPhone failed:", error);
1734
- return null;
1735
- }
1736
- }
1737
- setupAppearanceWatcher() {
1738
- this.appearanceWatcherDispose?.();
1739
- if (typeof import_sdk_react.themeParams.isDark?.sub === "function") {
1740
- const disposer = import_sdk_react.themeParams.isDark.sub(() => {
1741
- const appearance = import_sdk_react.themeParams.isDark() ? "dark" : "light";
1742
- this.environment.appearance = appearance;
1743
- this.notifyAppearance(appearance);
1744
- });
1745
- this.appearanceWatcherDispose = this.registerDisposable(disposer);
1746
- }
1747
- }
1748
- notifyAppearance(appearance) {
1749
- for (const listener of this.appearanceListeners) {
1750
- listener(appearance);
1751
- }
1752
- }
1753
- setupActiveWatcher() {
1754
- this.activeWatcherDispose?.();
1755
- const activeSignal = import_sdk_react.miniApp.isActive;
1756
- const invoke = () => {
1757
- try {
1758
- const isActive = import_sdk_react.miniApp.isActive();
1759
- if (isActive) {
1760
- this.notifyViewRestore();
1761
- } else {
1762
- this.notifyViewHide();
1763
- }
1764
- } catch (error) {
1765
- console.warn("[tvm-app-adapter] miniApp.isActive() failed:", error);
1766
- }
1767
- };
1768
- if (typeof activeSignal?.sub === "function") {
1769
- const disposer = activeSignal.sub(() => invoke());
1770
- this.activeWatcherDispose = this.registerDisposable(disposer);
1771
- invoke();
1772
- return;
1773
- }
1774
- try {
1775
- invoke();
1776
- } catch {
1777
- }
1778
- }
1779
- async prepareViewport() {
1780
- try {
1781
- await bindViewportCssVars({
1782
- ...this.getViewportMountOptions(),
1783
- bindCssVars: typeof import_sdk_react.viewport.bindCssVars === "function" ? import_sdk_react.viewport.bindCssVars : void 0
1784
- });
1785
- } catch (error) {
1786
- console.warn("[tvm-app-adapter] prepareViewport failed:", error);
1787
- }
1788
- }
1789
- async requestFullscreenInternal() {
1790
- try {
1791
- const viewportOptions = this.getViewportMountOptions();
1792
- await ensureViewportMounted(viewportOptions);
1793
- const { sdkViewport } = viewportOptions;
1794
- const canUseRaw = typeof sdkViewport.isSupported === "function" ? sdkViewport.isSupported() : false;
1795
- if (canUseRaw && typeof sdkViewport.requestFullscreen === "function") {
1796
- await sdkViewport.requestFullscreen();
1797
- } else if (import_sdk_react.viewport.requestFullscreen && import_sdk_react.viewport.requestFullscreen.isAvailable?.()) {
1798
- await import_sdk_react.viewport.requestFullscreen();
1799
- } else {
1800
- (0, import_sdk_react.postEvent)("web_app_request_fullscreen");
1801
- }
1802
- this.disableVerticalSwipes();
1803
- } catch (error) {
1804
- console.warn("[tvm-app-adapter] Telegram requestFullscreen failed:", error);
1805
- }
1806
- }
1807
- getViewportMountOptions() {
1808
- const sdkViewport = import_sdk.viewport;
1809
- return {
1810
- sdkViewport,
1811
- fallbackMount: async () => {
1812
- if (import_sdk_react.viewport.mount?.isAvailable?.()) {
1813
- await import_sdk_react.viewport.mount();
1814
- }
1815
- }
1816
- };
1817
- }
1818
- safeHeightFromSdk() {
1819
- try {
1820
- if (typeof import_sdk.viewport.height === "function") {
1821
- return import_sdk.viewport.height();
1822
- }
1823
- } catch {
1824
- return void 0;
1825
- }
1826
- return void 0;
1827
- }
1828
- stableHeightFromSdk() {
1829
- try {
1830
- if (typeof import_sdk.viewport.stableHeight === "function") {
1831
- return import_sdk.viewport.stableHeight();
1832
- }
1833
- } catch {
1834
- return void 0;
1835
- }
1836
- return void 0;
1837
- }
1838
- notifyViewHide() {
1839
- for (const listener of this.viewHideListeners) {
1840
- try {
1841
- listener();
1842
- } catch (error) {
1843
- console.warn("[tvm-app-adapter] onViewHide listener failed:", error);
1844
- }
1845
- }
1846
- }
1847
- normalizeDecodedStartParam(startParam) {
1848
- let decoded;
1849
- try {
1850
- decoded = (0, import_sdk.decodeStartParam)(startParam);
1851
- } catch {
1852
- decoded = startParam;
1853
- }
1854
- if (decoded && typeof decoded === "object" && !Array.isArray(decoded)) {
1855
- return { ...decoded };
1856
- }
1857
- if (typeof decoded === "string" && decoded) {
1858
- const parsed = this.parseQueryString(decoded);
1859
- if (Object.keys(parsed).length) {
1860
- return parsed;
1861
- }
1862
- return { startParam: decoded };
1863
- }
1864
- return {};
1865
- }
1866
- parseQueryString(value) {
1867
- const normalized = value.startsWith("?") ? value.slice(1) : value;
1868
- const params = new URLSearchParams(normalized);
1869
- const result = {};
1870
- const keys = /* @__PURE__ */ new Set();
1871
- for (const [key] of params.entries()) {
1872
- keys.add(key);
1873
- }
1874
- for (const key of keys) {
1875
- const values = params.getAll(key);
1876
- if (!values.length) {
1877
- continue;
1878
- }
1879
- result[key] = values.length === 1 ? values[0] : values;
1880
- }
1881
- return result;
1882
- }
1883
- notifyViewRestore() {
1884
- for (const listener of this.viewRestoreListeners) {
1885
- try {
1886
- listener();
1887
- } catch (error) {
1888
- console.warn("[tvm-app-adapter] onViewRestore listener failed:", error);
1889
- }
1890
- }
1891
- }
1892
- onDestroy() {
1893
- this.appearanceWatcherDispose?.();
1894
- this.appearanceWatcherDispose = void 0;
1895
- this.activeWatcherDispose?.();
1896
- this.activeWatcherDispose = void 0;
1897
- this.appearanceListeners.clear();
1898
- this.viewHideListeners.clear();
1899
- this.viewRestoreListeners.clear();
1900
- this.backHandlers.clear();
1901
- super.onDestroy();
1902
- }
1903
- };
1904
-
1905
- // src/adapters/vkAdapter.ts
1906
- var import_vk_bridge = __toESM(require("@vkontakte/vk-bridge"), 1);
1907
-
1908
- // src/config/vkAnalytics.ts
1909
- var pixelCode = null;
1910
- function setVkPixelCode(next) {
1911
- if (typeof next !== "string") {
1912
- pixelCode = null;
1913
- return;
1914
- }
1915
- const normalized = next.trim();
1916
- pixelCode = normalized || null;
1917
- }
1918
- function getVkPixelCode() {
1919
- return pixelCode;
1920
- }
1921
-
1922
- // src/lib/bridge.ts
1923
- async function isBridgeMethodSupported(method, supportsAsync) {
1924
- if (typeof supportsAsync !== "function") {
1925
- return false;
1926
- }
1927
- try {
1928
- return await supportsAsync(method);
1929
- } catch (error) {
1930
- console.warn("[tvm-app-adapter] bridge.supportsAsync failed:", error);
1931
- return false;
1932
- }
1933
- }
1934
-
1935
- // src/adapters/vkAdapter.ts
1936
- var ANALYTICS_EVENT_NAME_PATTERN = /^[a-z0-9][a-z0-9_.:-]{0,63}$/i;
1937
- var ANALYTICS_FALLBACK_EVENT = "VK_ANALYTICS_EVENT";
1938
- var VKMiniAppAdapter = class extends BaseMiniAppAdapter {
1939
- constructor() {
1940
- super("vk");
1941
- __publicField(this, "configSafeArea");
1942
- __publicField(this, "stopViewportTracking");
1943
- __publicField(this, "supportsAsync", typeof import_vk_bridge.default.supportsAsync === "function" ? import_vk_bridge.default.supportsAsync.bind(import_vk_bridge.default) : void 0);
1944
- __publicField(this, "pixelCodeWarningShown", false);
1945
- __publicField(this, "unsubscribe");
1946
- __publicField(this, "launchParams");
1947
- __publicField(this, "queryParams");
1948
- __publicField(this, "viewHideListeners", /* @__PURE__ */ new Set());
1949
- __publicField(this, "viewRestoreListeners", /* @__PURE__ */ new Set());
1950
- }
1951
- computeSafeArea() {
1952
- const baseSafeArea = this.computeBaseSafeArea();
1953
- const overlayInsets = this.resolveOverlayInsets();
1954
- if (overlayInsets) {
1955
- return computeCombinedSafeArea({
1956
- environment: baseSafeArea,
1957
- minimum: overlayInsets
1958
- });
1959
- }
1960
- return baseSafeArea;
1961
- }
1962
- async init(_options) {
1963
- if (this.ready) {
1964
- return;
1965
- }
1966
- const handler = (event) => this.handleBridgeEvent(event);
1967
- import_vk_bridge.default.subscribe(handler);
1968
- this.unsubscribe = this.registerDisposable(() => import_vk_bridge.default.unsubscribe(handler));
1969
- let initialConfig;
1970
- try {
1971
- initialConfig = await import_vk_bridge.default.send("VKWebAppGetConfig");
1972
- } catch (error) {
1973
- console.warn("[tvm-app-adapter] VKWebAppGetConfig failed:", error);
1974
- }
1975
- try {
1976
- const initResult = await import_vk_bridge.default.send("VKWebAppInit");
1977
- if (initResult && "result" in initResult && initResult.result === false) {
1978
- console.warn("[tvm-app-adapter] VKWebAppInit returned result=false.");
1979
- }
1980
- } catch (error) {
1981
- console.error("[tvm-app-adapter] VKWebAppInit failed:", error);
1982
- this.unsubscribe?.();
1983
- this.unsubscribe = void 0;
1984
- throw error;
1985
- }
1986
- let launchParams;
1987
- try {
1988
- launchParams = await import_vk_bridge.default.send("VKWebAppGetLaunchParams");
1989
- } catch (error) {
1990
- console.error("[tvm-app-adapter] VKWebAppGetLaunchParams failed:", error);
1991
- this.unsubscribe?.();
1992
- this.unsubscribe = void 0;
1993
- throw error;
1994
- }
1995
- const search = typeof window !== "undefined" ? window.location.search : "";
1996
- const queryParams = (0, import_vk_bridge.parseURLSearchParamsForGetLaunchParams)(search);
1997
- this.launchParams = launchParams;
1998
- this.queryParams = queryParams;
1999
- this.environment = this.composeEnvironment(launchParams, queryParams, initialConfig);
2000
- this.configSafeArea = this.environment.safeArea;
2001
- const combinedSafeArea = this.computeSafeArea();
2002
- this.environment.safeArea = combinedSafeArea;
2003
- this.applyAppearance(this.environment.appearance, initialConfig?.scheme);
2004
- this.notifyEnvironmentChanged();
2005
- this.ready = true;
2006
- this.startViewportTracking();
2007
- }
2008
- async vibrateImpact(style) {
2009
- if (await this.supportsBridgeMethod("VKWebAppTapticImpactOccurred")) {
2010
- import_vk_bridge.default.send("VKWebAppTapticImpactOccurred", { style });
2011
- }
2012
- }
2013
- async vibrateNotification(type) {
2014
- if (await this.supportsBridgeMethod("VKWebAppTapticNotificationOccurred")) {
2015
- import_vk_bridge.default.send("VKWebAppTapticNotificationOccurred", { type });
2016
- }
2017
- }
2018
- async vibrateSelection() {
2019
- if (await this.supportsBridgeMethod("VKWebAppTapticSelectionChanged")) {
2020
- import_vk_bridge.default.send("VKWebAppTapticSelectionChanged");
2021
- }
2022
- }
2023
- async setColors(colors) {
2024
- const { header, background } = colors;
2025
- if (header || background) {
2026
- const canApplyViewSettings = await this.supportsBridgeMethod("VKWebAppSetViewSettings");
2027
- if (canApplyViewSettings) {
2028
- const statusBarStyle = header ? this.resolveStatusBarStyle(header) : this.environment.appearance?.includes("dark") ? "light" : "dark";
2029
- await import_vk_bridge.default.send("VKWebAppSetViewSettings", {
2030
- status_bar_style: statusBarStyle,
2031
- ...header ? { action_bar_color: header } : {},
2032
- ...background ? { navigation_bar_color: background } : {}
2033
- });
2034
- }
2035
- }
2036
- await super.setColors(colors);
2037
- }
2038
- getEnvironment() {
2039
- return {
2040
- ...this.environment,
2041
- isWebView: import_vk_bridge.default.isWebView()
2042
- };
2043
- }
2044
- getLaunchParams() {
2045
- if (!this.launchParams) {
2046
- return {
2047
- customLaunchParams: this.readCustomUrlParams((key) => {
2048
- const normalized = key.toLowerCase();
2049
- return normalized.startsWith("vk_") || normalized === "sign";
2050
- })
2051
- };
2052
- }
2053
- return {
2054
- launchParams: this.launchParams,
2055
- customLaunchParams: this.readCustomUrlParams((key) => {
2056
- const normalized = key.toLowerCase();
2057
- return normalized.startsWith("vk_") || normalized === "sign";
2058
- })
2059
- };
2060
- }
2061
- async openExternalLink(url) {
2062
- const a = document.createElement("a");
2063
- a.href = url;
2064
- a.target = "_blank";
2065
- a.rel = "noopener noreferrer";
2066
- a.style.display = "none";
2067
- document.body.appendChild(a);
2068
- a.click();
2069
- a.remove();
2070
- }
2071
- async supports(capability) {
2072
- switch (capability) {
2073
- case "haptics": {
2074
- const [impact, notification, selection] = await Promise.all([
2075
- this.supportsBridgeMethod("VKWebAppTapticImpactOccurred"),
2076
- this.supportsBridgeMethod("VKWebAppTapticNotificationOccurred"),
2077
- this.supportsBridgeMethod("VKWebAppTapticSelectionChanged")
2078
- ]);
2079
- return impact || notification || selection;
2080
- }
2081
- case "qrScanner":
2082
- return this.supportsBridgeMethod("VKWebAppOpenCodeReader");
2083
- case "requestPhone": {
2084
- const [supportsPhoneNumber, supportsPersonalCard] = await Promise.all([
2085
- this.supportsBridgeMethod("VKWebAppGetPhoneNumber"),
2086
- this.supportsBridgeMethod("VKWebAppGetPersonalCard")
2087
- ]);
2088
- return supportsPhoneNumber || supportsPersonalCard;
2089
- }
2090
- case "notifications":
2091
- return this.supportsBridgeMethod("VKWebAppAllowNotifications");
2092
- case "shareUrl":
2093
- return this.supportsBridgeMethod("VKWebAppShare");
2094
- case "shareStory":
2095
- return this.supportsBridgeMethod("VKWebAppShowStoryBox");
2096
- case "downloadFile":
2097
- return this.supportsBridgeMethod("VKWebAppDownloadFile");
2098
- case "addToHomeScreen":
2099
- return this.supportsBridgeMethod("VKWebAppAddToHomeScreen");
2100
- case "denyNotifications":
2101
- return this.supportsBridgeMethod("VKWebAppDenyNotifications");
2102
- case "openExternalLink":
2103
- return true;
2104
- case "viewVisibility":
2105
- return true;
2106
- default:
2107
- return await super.supports(capability);
2108
- }
2109
- }
2110
- async requestPhone() {
2111
- const [supportsPhoneNumber, supportsPersonalCard] = await Promise.all([
2112
- this.supportsBridgeMethod("VKWebAppGetPhoneNumber"),
2113
- this.supportsBridgeMethod("VKWebAppGetPersonalCard")
2114
- ]);
2115
- if (!supportsPhoneNumber && !supportsPersonalCard) {
2116
- return super.requestPhone();
2117
- }
2118
- try {
2119
- if (supportsPhoneNumber) {
2120
- const result = await import_vk_bridge.default.send("VKWebAppGetPhoneNumber");
2121
- const phoneNumber = result.phone_number;
2122
- return typeof phoneNumber === "string" && phoneNumber ? phoneNumber : null;
2123
- }
2124
- const card = await import_vk_bridge.default.send("VKWebAppGetPersonalCard", { type: ["phone"] });
2125
- const phone = card.phone;
2126
- return typeof phone === "string" && phone ? phone : null;
2127
- } catch (error) {
2128
- console.warn("[tvm-app-adapter] VK requestPhone failed:", error);
2129
- return null;
2130
- }
2131
- }
2132
- async requestNotificationsPermission() {
2133
- const supported = await this.supportsBridgeMethod("VKWebAppAllowNotifications");
2134
- if (!supported) {
2135
- return super.requestNotificationsPermission();
2136
- }
2137
- try {
2138
- const response = await import_vk_bridge.default.send("VKWebAppAllowNotifications");
2139
- if (response && typeof response === "object" && "result" in response) {
2140
- return Boolean(response.result);
2141
- }
2142
- return true;
2143
- } catch (error) {
2144
- console.warn("[tvm-app-adapter] VK allow notifications failed:", error);
2145
- return false;
2146
- }
2147
- }
2148
- async addToHomeScreen() {
2149
- const supported = await this.supportsBridgeMethod("VKWebAppAddToHomeScreen");
2150
- if (!supported) {
2151
- console.warn("[tvm-app-adapter] VK addToHomeScreen not supported");
2152
- return super.addToHomeScreen();
2153
- }
2154
- try {
2155
- const response = await import_vk_bridge.default.send("VKWebAppAddToHomeScreen");
2156
- if (response && typeof response === "object" && "result" in response) {
2157
- return Boolean(response.result);
2158
- }
2159
- return true;
2160
- } catch (error) {
2161
- console.warn("[tvm-app-adapter] VK addToHomeScreen failed:", error);
2162
- return false;
2163
- }
2164
- }
2165
- async denyNotifications() {
2166
- const supported = await this.supportsBridgeMethod("VKWebAppDenyNotifications");
2167
- if (!supported) {
2168
- return super.denyNotifications();
2169
- }
2170
- try {
2171
- const response = await import_vk_bridge.default.send("VKWebAppDenyNotifications");
2172
- if (response && typeof response === "object" && "result" in response) {
2173
- return Boolean(response.result);
2174
- }
2175
- return true;
2176
- } catch (error) {
2177
- console.warn("[tvm-app-adapter] VK deny notifications failed:", error);
2178
- return false;
2179
- }
2180
- }
2181
- async scanQRCode(options) {
2182
- const supportsQrScanner = await this.supportsBridgeMethod("VKWebAppOpenCodeReader");
2183
- if (!supportsQrScanner) {
2184
- return super.scanQRCode(options);
2185
- }
2186
- let result = null;
2187
- try {
2188
- const data = await import_vk_bridge.default.send("VKWebAppOpenCodeReader");
2189
- if (data.code_data) {
2190
- result = data.code_data;
2191
- }
2192
- } catch (error) {
2193
- console.log(error);
2194
- }
2195
- return result;
2196
- }
2197
- onViewHide(callback) {
2198
- this.viewHideListeners.add(callback);
2199
- return () => {
2200
- this.viewHideListeners.delete(callback);
2201
- };
2202
- }
2203
- onViewRestore(callback) {
2204
- this.viewRestoreListeners.add(callback);
2205
- return () => {
2206
- this.viewRestoreListeners.delete(callback);
2207
- };
2208
- }
2209
- async shareStory(mediaUrl, _options) {
2210
- const options = _options;
2211
- const vkOptions = options?.vk;
2212
- const fallbackAttachment = options?.link ? {
2213
- type: "url",
2214
- text: "open",
2215
- url: options.link.url
2216
- } : void 0;
2217
- const fallbackStickers = options?.text ? [{
2218
- sticker_type: "native",
2219
- sticker: {
2220
- action_type: "text",
2221
- action: {
2222
- text: options.text,
2223
- style: "classic",
2224
- background_style: "none"
2225
- },
2226
- transform: {
2227
- gravity: "center_bottom",
2228
- translation_y: -0.2
2229
- }
2230
- }
2231
- }] : void 0;
2232
- const bridgeOptions = {
2233
- background_type: vkOptions?.backgroundType ?? "image",
2234
- url: mediaUrl,
2235
- locked: vkOptions?.locked ?? true,
2236
- ...vkOptions?.attachment ?? fallbackAttachment ? { attachment: vkOptions?.attachment ?? fallbackAttachment } : {},
2237
- ...vkOptions?.stickers ?? fallbackStickers ? { stickers: vkOptions?.stickers ?? fallbackStickers } : {}
2238
- };
2239
- await import_vk_bridge.default.send("VKWebAppShowStoryBox", bridgeOptions);
2240
- }
2241
- shareUrl(url, text) {
2242
- void this.shareUrlInternal(url, text);
2243
- }
2244
- async shareUrlInternal(url, text) {
2245
- const supported = await this.supportsBridgeMethod("VKWebAppShare");
2246
- if (!supported) {
2247
- super.shareUrl(url, text ?? "");
2248
- return;
2249
- }
2250
- try {
2251
- await import_vk_bridge.default.send("VKWebAppShare", {
2252
- link: url,
2253
- ...text ? { text } : {}
2254
- });
2255
- } catch (error) {
2256
- console.warn("[tvm-app-adapter] VK shareUrl failed:", error);
2257
- super.shareUrl(url, text ?? "");
2258
- }
2259
- }
2260
- async downloadFile(url, filename) {
2261
- const supported = await this.supportsBridgeMethod("VKWebAppDownloadFile");
2262
- if (!supported) {
2263
- await super.downloadFile(url, filename);
2264
- return;
2265
- }
2266
- try {
2267
- const response = await import_vk_bridge.default.send("VKWebAppDownloadFile", { url, filename });
2268
- const result = response?.result;
2269
- if (result === false) {
2270
- throw new Error("VKWebAppDownloadFile returned result=false");
2271
- }
2272
- } catch (error) {
2273
- console.warn("[tvm-app-adapter] VK downloadFile failed:", error);
2274
- await super.downloadFile(url, filename);
2275
- }
2276
- }
2277
- trackConversionEvent(event, payload) {
2278
- const normalizedEvent = this.normalizeAnalyticsEventName(event);
2279
- if (!normalizedEvent) {
2280
- return;
2281
- }
2282
- const envelope = {
2283
- method: "VKWebAppConversionHit",
2284
- params: {
2285
- event: normalizedEvent,
2286
- params: this.normalizeAnalyticsPayload(payload)
2287
- }
2288
- };
2289
- this.dispatchAnalytics(envelope);
2290
- }
2291
- trackPixelEvent(event, payload) {
2292
- const pixelCode2 = getVkPixelCode();
2293
- if (!pixelCode2) {
2294
- if (!this.pixelCodeWarningShown) {
2295
- console.warn("[VKAnalytics] VK pixel code is not configured. Call configureVkPixel() before tracking.");
2296
- this.pixelCodeWarningShown = true;
2297
- }
2298
- return;
2299
- }
2300
- const normalizedEvent = this.normalizeAnalyticsEventName(event);
2301
- if (!normalizedEvent) {
2302
- return;
2303
- }
2304
- const envelope = {
2305
- method: "VKWebAppRetargetingPixel",
2306
- params: {
2307
- pixel_code: pixelCode2,
2308
- type: normalizedEvent,
2309
- data: this.normalizeAnalyticsPayload(payload)
2310
- }
2311
- };
2312
- this.pixelCodeWarningShown = false;
2313
- this.dispatchAnalytics(envelope);
2314
- }
2315
- dispatchAnalytics(envelope) {
2316
- if (typeof import_vk_bridge.default.isWebView === "function") {
2317
- try {
2318
- if (!import_vk_bridge.default.isWebView()) {
2319
- this.emitAnalyticsFallback(envelope);
2320
- return;
2321
- }
2322
- } catch (error) {
2323
- console.warn("[VKAnalytics] bridge.isWebView check failed:", error);
2324
- this.emitAnalyticsFallback(envelope);
2325
- return;
2326
- }
2327
- }
2328
- void this.safeBridgeSend(envelope.method, envelope.params);
2329
- }
2330
- emitAnalyticsFallback(envelope) {
2331
- if (typeof window === "undefined") {
2332
- return;
2333
- }
2334
- const detail = {
2335
- ...envelope,
2336
- timestamp: Date.now()
2337
- };
2338
- if (typeof window.dispatchEvent === "function" && typeof window.CustomEvent === "function") {
2339
- window.dispatchEvent(new CustomEvent(ANALYTICS_FALLBACK_EVENT, { detail }));
2340
- return;
2341
- }
2342
- try {
2343
- window.postMessage({ type: ANALYTICS_FALLBACK_EVENT, detail }, "*");
2344
- } catch (error) {
2345
- console.warn("[VKAnalytics] fallback dispatch failed", error);
2346
- }
2347
- }
2348
- normalizeAnalyticsEventName(event) {
2349
- if (typeof event !== "string") {
2350
- return null;
2351
- }
2352
- const trimmed = event.trim();
2353
- if (!trimmed || !ANALYTICS_EVENT_NAME_PATTERN.test(trimmed)) {
2354
- console.warn(`[VKAnalytics] Invalid event name: "${event}"`);
2355
- return null;
2356
- }
2357
- return trimmed;
2358
- }
2359
- normalizeAnalyticsPayload(payload) {
2360
- if (!this.isPlainObject(payload)) {
2361
- return {};
2362
- }
2363
- return { ...payload };
2364
- }
2365
- isPlainObject(value) {
2366
- if (value === null || typeof value !== "object") {
2367
- return false;
2368
- }
2369
- if (Array.isArray(value)) {
2370
- return false;
2371
- }
2372
- const proto = Object.getPrototypeOf(value);
2373
- return proto === Object.prototype || proto === null;
2374
- }
2375
- async safeBridgeSend(method, params) {
2376
- try {
2377
- const supported = await this.supportsBridgeMethod(method);
2378
- if (!supported) {
2379
- return;
2380
- }
2381
- await import_vk_bridge.default.send(method, params);
2382
- } catch (error) {
2383
- console.warn(`[VKAnalytics] ${method} failed`, error);
2384
- }
2385
- }
2386
- composeEnvironment(launchParams, queryParams, config) {
2387
- const language = queryParams.vk_language ?? launchParams.vk_language;
2388
- const platform = queryParams.vk_platform ?? launchParams.vk_platform;
2389
- const appId = queryParams.vk_app_id ?? launchParams.vk_app_id;
2390
- const prefersDark = typeof window !== "undefined" && typeof window.matchMedia === "function" ? window.matchMedia("(prefers-color-scheme: dark)").matches : false;
2391
- const appearance = this.normalizeAppearance(config?.appearance, config?.scheme) ?? (prefersDark ? "dark" : "light");
2392
- const configSafeArea = this.extractSafeAreaFromConfig(config);
2393
- return {
2394
- platform: "vk",
2395
- sdkVersion: platform ? String(platform) : void 0,
2396
- appVersion: typeof appId === "number" ? `vk-app-${appId}` : void 0,
2397
- languageCode: language ? String(language) : void 0,
2398
- appearance,
2399
- isWebView: import_vk_bridge.default.isWebView(),
2400
- safeArea: configSafeArea
2401
- };
2402
- }
2403
- handleBridgeEvent(event) {
2404
- const { type, data } = event.detail ?? {};
2405
- if (type === "VKWebAppViewHide") {
2406
- this.notifyVisibilityListeners(this.viewHideListeners);
2407
- return;
2408
- }
2409
- if (type === "VKWebAppViewRestore") {
2410
- this.notifyVisibilityListeners(this.viewRestoreListeners);
2411
- return;
2412
- }
2413
- if (type === "VKWebAppUpdateConfig" && data) {
2414
- const config = data;
2415
- this.updateEnvironmentFromConfig(config);
2416
- }
2417
- }
2418
- updateEnvironmentFromConfig(config) {
2419
- if (!this.environment) {
2420
- return;
2421
- }
2422
- const nextAppearance = this.normalizeAppearance(config.appearance, config.scheme);
2423
- let changed = false;
2424
- if (nextAppearance && nextAppearance !== this.environment.appearance) {
2425
- this.environment.appearance = nextAppearance;
2426
- changed = true;
2427
- }
2428
- const prevSafeArea = this.environment.safeArea;
2429
- const nextSafeArea = this.extractSafeAreaFromConfig(config);
2430
- if (nextSafeArea) {
2431
- this.configSafeArea = nextSafeArea;
2432
- this.environment.safeArea = nextSafeArea;
2433
- } else {
2434
- this.configSafeArea = void 0;
2435
- this.environment.safeArea = void 0;
2436
- }
2437
- const combinedSafeArea = this.computeSafeArea() ?? {
2438
- top: 0,
2439
- right: 0,
2440
- bottom: 0,
2441
- left: 0
2442
- };
2443
- const prevForComparison = prevSafeArea;
2444
- const safeAreaChanged = !prevForComparison || prevForComparison.top !== combinedSafeArea.top || prevForComparison.right !== combinedSafeArea.right || prevForComparison.bottom !== combinedSafeArea.bottom || prevForComparison.left !== combinedSafeArea.left;
2445
- if (safeAreaChanged) {
2446
- this.environment.safeArea = combinedSafeArea;
2447
- changed = true;
2448
- } else {
2449
- this.environment.safeArea = prevSafeArea;
2450
- }
2451
- this.applyAppearance(this.environment.appearance, config.scheme);
2452
- if (changed) {
2453
- this.notifyEnvironmentChanged();
2454
- }
2455
- }
2456
- applyAppearance(appearance, scheme) {
2457
- if (typeof document === "undefined") {
2458
- return;
2459
- }
2460
- if (appearance) {
2461
- document.documentElement.dataset.vkAppearance = appearance;
2462
- document.documentElement.classList.toggle("dark", appearance === "dark");
2463
- }
2464
- if (scheme) {
2465
- document.documentElement.dataset.vkScheme = scheme;
2466
- if (!appearance) {
2467
- const normalized = this.normalizeAppearance(void 0, scheme);
2468
- document.documentElement.classList.toggle("dark", normalized === "dark");
2469
- }
2470
- }
2471
- }
2472
- resolveStatusBarStyle(color) {
2473
- const hex = color.replace("#", "");
2474
- const normalized = hex.length === 3 ? hex.split("").map((symbol) => symbol + symbol).join("") : hex.slice(0, 6);
2475
- const r = parseInt(normalized.slice(0, 2), 16) / 255;
2476
- const g = parseInt(normalized.slice(2, 4), 16) / 255;
2477
- const b = parseInt(normalized.slice(4, 6), 16) / 255;
2478
- const luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b;
2479
- return luminance > 0.6 ? "dark" : "light";
2480
- }
2481
- normalizeAppearance(rawAppearance, scheme) {
2482
- const normalized = rawAppearance?.toLowerCase();
2483
- if (normalized === "dark" || normalized === "light") {
2484
- return normalized;
2485
- }
2486
- const normalizedScheme = scheme?.toLowerCase();
2487
- if (normalizedScheme) {
2488
- if (normalizedScheme.includes("dark") || normalizedScheme.includes("space_gray")) {
2489
- return "dark";
2490
- }
2491
- return "light";
2492
- }
2493
- return void 0;
2494
- }
2495
- extractSafeAreaFromConfig(config) {
2496
- const rawInsets = config && "insets" in config ? config.insets : void 0;
2497
- if (!rawInsets) {
2498
- return void 0;
2499
- }
2500
- const { top = 0, right = 0, bottom = 0, left = 0 } = rawInsets;
2501
- const values = [top, right, bottom, left].map((value) => typeof value === "number" ? value : Number(value) || 0);
2502
- const hasInsets = values.some((value) => value !== 0);
2503
- if (!hasInsets) {
2504
- return void 0;
2505
- }
2506
- const [nTop, nRight, nBottom, nLeft] = values;
2507
- return { top: nTop, right: nRight, bottom: nBottom, left: nLeft };
2508
- }
2509
- notifyVisibilityListeners(listeners) {
2510
- for (const listener of listeners) {
2511
- try {
2512
- listener();
2513
- } catch (error) {
2514
- console.warn("[tvm-app-adapter] VK visibility listener failed:", error);
2515
- }
2516
- }
2517
- }
2518
- computeBaseSafeArea() {
2519
- return computeCombinedSafeArea({
2520
- environment: this.configSafeArea,
2521
- viewport: this.getViewportInsets?.(),
2522
- css: readCssSafeArea()
2523
- });
2524
- }
2525
- startViewportTracking() {
2526
- this.stopViewportTracking?.();
2527
- const dispose = createSafeAreaWatcher({
2528
- getSafeArea: () => this.computeSafeArea(),
2529
- onChange: (next) => {
2530
- this.environment.safeArea = next;
2531
- this.notifyEnvironmentChanged();
2532
- }
2533
- });
2534
- if (dispose) {
2535
- this.stopViewportTracking = this.registerDisposable(dispose);
2536
- }
2537
- }
2538
- resolveOverlayInsets() {
2539
- if (typeof window === "undefined") {
2540
- return void 0;
2541
- }
2542
- if (!import_vk_bridge.default.isWebView()) {
2543
- return void 0;
2544
- }
2545
- if (!this.isLikelyMobilePlatform()) {
2546
- return void 0;
2547
- }
2548
- const viewportWidth = window.innerWidth || document.documentElement?.clientWidth || 0;
2549
- if (!viewportWidth) {
2550
- return void 0;
2551
- }
2552
- const overlayBreakpoint = 880;
2553
- if (viewportWidth > overlayBreakpoint) {
2554
- return void 0;
2555
- }
2556
- const orientationQuery = window.matchMedia?.("(orientation: landscape)");
2557
- const isLandscape = Boolean(orientationQuery?.matches);
2558
- const top = isLandscape ? 48 : 56;
2559
- const right = isLandscape ? 72 : 88;
2560
- return { top, right };
2561
- }
2562
- isLikelyMobilePlatform() {
2563
- const platform = (this.resolveLaunchParam("vk_platform") ?? "").toLowerCase();
2564
- const device = (this.resolveLaunchParam("vk_viewer_device") ?? "").toLowerCase();
2565
- const isLayer = this.resolveLaunchParam("vk_is_layer") === "1";
2566
- if (isLayer) {
2567
- return false;
2568
- }
2569
- const mobilePattern = /(iphone|ipad|ios|android|mobile)/i;
2570
- const desktopPattern = /(desktop|web|tablet)/i;
2571
- const matchesMobilePlatform = mobilePattern.test(platform) || mobilePattern.test(device);
2572
- const matchesDesktopPlatform = desktopPattern.test(platform) || desktopPattern.test(device);
2573
- if (!matchesMobilePlatform) {
2574
- return false;
2575
- }
2576
- return !matchesDesktopPlatform;
2577
- }
2578
- resolveLaunchParam(key) {
2579
- const queryValue = this.queryParams?.[key];
2580
- if (typeof queryValue === "string" && queryValue) {
2581
- return queryValue;
2582
- }
2583
- const launchValue = this.launchParams ? this.launchParams[key] : void 0;
2584
- if (typeof launchValue === "string" && launchValue) {
2585
- return launchValue;
2586
- }
2587
- if (typeof launchValue === "number") {
2588
- return String(launchValue);
2589
- }
2590
- if (typeof launchValue === "boolean") {
2591
- return launchValue ? "1" : "0";
2592
- }
2593
- return void 0;
2594
- }
2595
- supportsBridgeMethod(method) {
2596
- return isBridgeMethodSupported(method, this.supportsAsync);
2597
- }
2598
- onDestroy() {
2599
- this.unsubscribe?.();
2600
- this.unsubscribe = void 0;
2601
- this.stopViewportTracking?.();
2602
- this.stopViewportTracking = void 0;
2603
- this.viewHideListeners.clear();
2604
- this.viewRestoreListeners.clear();
2605
- super.onDestroy();
2606
- }
2607
- };
2608
-
2609
- // src/adapters/webAdapter.ts
2610
- var import_jsqr = __toESM(require("jsqr"), 1);
2611
- var WebMiniAppAdapter = class extends BaseMiniAppAdapter {
2612
- constructor() {
2613
- super("web", {
2614
- sdkVersion: navigator.userAgent,
2615
- languageCode: navigator.language,
2616
- isWebView: false
2617
- });
2618
- __publicField(this, "deferredPrompt", null);
2619
- if (typeof window !== "undefined") {
2620
- window.addEventListener("beforeinstallprompt", (event) => {
2621
- event.preventDefault();
2622
- this.deferredPrompt = event;
2623
- });
2624
- }
2625
- }
2626
- supports(capability) {
2627
- switch (capability) {
2628
- case "copyTextToClipboard":
2629
- return typeof navigator !== "undefined" && Boolean(navigator.clipboard?.writeText);
2630
- case "downloadFile":
2631
- return typeof document !== "undefined";
2632
- case "shareUrl":
2633
- return typeof navigator !== "undefined" && (Boolean(navigator.share) || Boolean(navigator.clipboard?.writeText));
2634
- case "addToHomeScreen":
2635
- return /android/i.test(navigator.userAgent) && Boolean(this.deferredPrompt);
2636
- case "checkHomeScreenStatus":
2637
- return true;
2638
- default:
2639
- return super.supports(capability);
2640
- }
2641
- }
2642
- async downloadFile(url, filename) {
2643
- try {
2644
- await triggerFileDownload(url, filename, { preferBlob: true });
2645
- } catch (error) {
2646
- console.warn("[mini-app-template] Web downloadFile fallback:", error);
2647
- await super.downloadFile(url, filename);
2648
- }
2649
- }
2650
- scanQRCode(_options) {
2651
- return new Promise(async (resolve) => {
2652
- if (typeof document === "undefined") {
2653
- resolve(null);
2654
- return;
2655
- }
2656
- const prevOverflow = document.body.style.overflow;
2657
- const prevPos = document.body.style.position;
2658
- const prevTouch = document.body.style.touchAction;
2659
- const prevWidth = document.body.style.width;
2660
- const prevHtmlOverflow = document.documentElement.style.overflow;
2661
- document.body.style.overflow = "hidden";
2662
- document.body.style.position = "fixed";
2663
- document.body.style.width = "100%";
2664
- document.body.style.touchAction = "none";
2665
- document.documentElement.style.overflow = "hidden";
2666
- const overlay = document.createElement("div");
2667
- overlay.id = "qr-overlay";
2668
- overlay.style.position = "fixed";
2669
- overlay.style.top = "0";
2670
- overlay.style.left = "0";
2671
- overlay.style.right = "0";
2672
- overlay.style.bottom = "0";
2673
- overlay.style.zIndex = "999999999";
2674
- overlay.style.background = "rgba(0,0,0,0.92)";
2675
- overlay.style.width = "100%";
2676
- overlay.style.height = "100%";
2677
- overlay.style.display = "flex";
2678
- overlay.style.flexDirection = "column";
2679
- overlay.style.alignItems = "center";
2680
- overlay.style.justifyContent = "center";
2681
- overlay.style.gap = "20px";
2682
- overlay.style.padding = "24px";
2683
- overlay.style.boxSizing = "border-box";
2684
- overlay.style.overflow = "hidden";
2685
- overlay.style.backdropFilter = "blur(3px)";
2686
- document.body.appendChild(overlay);
2687
- const closeBtn = document.createElement("button");
2688
- closeBtn.innerText = "\u2715";
2689
- closeBtn.style.position = "absolute";
2690
- closeBtn.style.top = "22px";
2691
- closeBtn.style.right = "22px";
2692
- closeBtn.style.fontSize = "32px";
2693
- closeBtn.style.color = "white";
2694
- closeBtn.style.background = "transparent";
2695
- closeBtn.style.border = "none";
2696
- closeBtn.style.cursor = "pointer";
2697
- closeBtn.style.zIndex = "9999999999";
2698
- overlay.appendChild(closeBtn);
2699
- const scanSize = Math.min(Math.floor(Math.min(window.innerWidth, window.innerHeight) * 0.72), 320);
2700
- const scanBox = document.createElement("div");
2701
- scanBox.style.width = `${scanSize}px`;
2702
- scanBox.style.height = `${scanSize}px`;
2703
- scanBox.style.position = "relative";
2704
- scanBox.style.flex = "0 0 auto";
2705
- scanBox.style.borderRadius = "18px";
2706
- scanBox.style.overflow = "hidden";
2707
- overlay.appendChild(scanBox);
2708
- const scanArea = document.createElement("div");
2709
- scanArea.style.position = "absolute";
2710
- scanArea.style.inset = "0";
2711
- scanArea.style.zIndex = "1";
2712
- scanArea.style.background = "#000";
2713
- scanBox.appendChild(scanArea);
2714
- const video = document.createElement("video");
2715
- video.setAttribute("playsinline", "true");
2716
- video.autoplay = true;
2717
- video.muted = true;
2718
- video.style.width = "100%";
2719
- video.style.height = "100%";
2720
- video.style.objectFit = "cover";
2721
- video.style.position = "absolute";
2722
- video.style.inset = "0";
2723
- scanArea.appendChild(video);
2724
- const frame = document.createElement("div");
2725
- frame.style.position = "absolute";
2726
- frame.style.top = "0";
2727
- frame.style.left = "0";
2728
- frame.style.right = "0";
2729
- frame.style.bottom = "0";
2730
- frame.style.border = "3px solid rgba(255,255,255,0.9)";
2731
- frame.style.borderRadius = "18px";
2732
- frame.style.pointerEvents = "none";
2733
- frame.style.zIndex = "3";
2734
- scanBox.appendChild(frame);
2735
- const line = document.createElement("div");
2736
- line.style.position = "absolute";
2737
- line.style.left = "0";
2738
- line.style.right = "0";
2739
- line.style.height = "2px";
2740
- line.style.background = "rgba(255,255,255,0.85)";
2741
- line.style.borderRadius = "2px";
2742
- line.style.animation = "qr-line 2s infinite";
2743
- line.style.zIndex = "4";
2744
- scanBox.appendChild(line);
2745
- const styleTag = document.createElement("style");
2746
- styleTag.innerHTML = `
3
+ `);return window.alert(e),n.buttons?.[0]?.id??"ok"}async scanQRCode(n){return null}async requestPhone(){return null}async requestNotificationsPermission(){if(typeof Notification>"u"||typeof Notification.requestPermission!="function")return!1;try{return await Notification.requestPermission()==="granted"}catch(n){return console.warn("[tvm-app-adapter] requestNotificationsPermission fallback failed:",n),!1}}async addToHomeScreen(){return!1}async checkHomeScreenStatus(){return"unknown"}async denyNotifications(){return console.warn("[tvm-app-adapter] denyNotifications fallback is not supported in this environment."),!1}enableVerticalSwipes(){}disableVerticalSwipes(){}notifyEnvironmentChanged(){for(let n of this.listeners)try{n()}catch(e){console.warn("[tvm-app-adapter] environment listener failed:",e)}}onDestroy(){}registerDisposable(n){return this.disposables.add(n)}readCustomUrlParams(n){if(typeof window>"u")return{};let e={},t=o=>{let p=new Set;for(let[d]of o.entries())p.add(d);for(let d of p){if(n?.(d))continue;let l=o.getAll(d);l.length&&(e[d]=l.length===1?l[0]:l)}};t(new URLSearchParams(window.location.search));let r=window.location.hash.startsWith("#")?window.location.hash.slice(1):window.location.hash;return r&&r.includes("=")&&t(new URLSearchParams(r)),e}applyScrollGuards(){if(typeof document>"u")return;let n=document.documentElement,e=document.body;if(!n||!e)return;let t=n.style.overscrollBehaviorY,r=e.style.overscrollBehaviorY,o=e.style.touchAction;return n.style.overscrollBehaviorY="none",e.style.overscrollBehaviorY="none",e.style.touchAction="manipulation",()=>{n.style.overscrollBehaviorY=t,e.style.overscrollBehaviorY=r,e.style.touchAction=o}}};var _=class extends S{constructor(n){super(n,{isWebView:!0,hasNativeQR:!0,hasPush:!0,hasWidgets:!0})}async supports(n){switch(n){case"qrScanner":return!0;case"notifications":return!0;default:return super.supports(n)}}async scanQRCode(){try{return await this.shell.openNativeQR()??null}catch(n){return console.warn("[tvm-app-adapter] shell.openNativeQR failed:",n),null}}async requestNotificationsPermission(){return te()}};var a=require("@tma.js/sdk-react"),c=require("@tma.js/sdk");function V(i){if(typeof i!="function")return!1;let n=i;if(typeof n.isAvailable=="function")try{return n.isAvailable()}catch(e){return console.warn("[tvm-app-adapter] feature availability check failed:",e),!1}return!0}function x(i,...n){if(!V(i))return{ok:!1};try{return{ok:!0,value:i(...n)}}catch(e){return console.warn("[tvm-app-adapter] feature call failed:",e),{ok:!1}}}async function ne(i){let{sdkViewport:n,fallbackMount:e}=i;if(n?.isSupported?.()){if(typeof n.isMounted=="function"&&n.isMounted())return;typeof n.mount=="function"&&await n.mount();return}typeof e=="function"&&await e()}async function He(i){if(await ne(i),typeof i.bindCssVars=="function")try{i.bindCssVars(i.mapper)}catch(n){if(n instanceof Error&&/css variables are already bound/i.test(n.message))return;throw n}}var W=class extends S{constructor(){super("telegram");h(this,"backHandlers",new Map);h(this,"cssVariablesBound",!1);h(this,"appearanceListeners",new Set);h(this,"appearanceWatcherDispose");h(this,"viewHideListeners",new Set);h(this,"viewRestoreListeners",new Set);h(this,"activeWatcherDispose")}async init(e){if(this.ready)return;let t=!!e?.debug,r=!!e?.eruda,o=!!e?.mockForMacOS;if((0,a.setDebug)(t),(0,a.init)(),a.miniApp.isSupported()||console.warn("[tvm-app-adapter] miniApp feature is not supported; falling back to limited mode."),r&&typeof window<"u"&&window.eruda&&(window.eruda.init(),window.eruda.position({x:window.innerWidth-150,y:window.innerHeight-150})),o){let s=!1;(0,a.mockTelegramEnv)({onEvent(u,v){if(u.name==="web_app_request_theme"){let m={};return s?m=a.themeParams.state():(s=!0,m||(m=(0,a.retrieveLaunchParams)().tgWebAppThemeParams)),(0,a.emitEvent)("theme_changed",{theme_params:m})}if(u.name==="web_app_request_safe_area")return(0,a.emitEvent)("safe_area_changed",{left:0,top:0,right:0,bottom:0});v()}})}a.initData.restore(),a.miniApp.ready();let p=(0,a.retrieveLaunchParams)(),d;try{d=a.miniApp.isDark()?"dark":"light"}catch{d=void 0}let l={platform:"telegram",sdkVersion:p.tgWebAppVersion,languageCode:a.initData.user()?.language_code,appearance:d,isWebView:!0};this.environment=l,this.notifyAppearance(l.appearance),a.backButton.mount.ifAvailable(),a.miniApp.mount.isAvailable()&&(a.themeParams.mount(),a.miniApp.mount(),this.bindCssVariables()),await this.prepareViewport(),this.setupAppearanceWatcher(),this.setupActiveWatcher(),this.registerDisposable(this.onViewportChange(()=>this.notifyEnvironmentChanged())),this.ready=!0}async setColors(e){let t={};if(e.header)if(a.miniApp.setHeaderColor.isAvailable()){let r=a.miniApp.setHeaderColor.supports?.("rgb")?e.header:"bg_color",{ok:o}=x(a.miniApp.setHeaderColor,r);o||(t.header=e.header)}else t.header=e.header;if(e.background)if(a.miniApp.setBgColor.isAvailable()){let{ok:r}=x(a.miniApp.setBgColor,e.background);r||(t.background=e.background)}else t.background=e.background;if(e.footer)if(a.miniApp.setBgColor.isAvailable()){let{ok:r}=x(a.miniApp.setBottomBarColorFp,e.footer);r||(t.footer=e.footer)}else t.footer=e.footer;(t.header||t.background)&&await super.setColors(t)}copyTextToClipboard(e){return(0,c.copyTextToClipboard)(e)}onBackButton(e){if(!a.backButton.isSupported())return super.onBackButton(e);let t=a.backButton.onClick(()=>e()),r=this.registerDisposable(()=>{typeof t=="function"&&t(),this.backHandlers.delete(e),this.backHandlers.size||a.backButton.hide()});return this.backHandlers.set(e,r),r}async openExternalLink(e){try{(0,a.openLink)(e,{tryInstantView:!0});return}catch{}await super.openExternalLink(e)}async openInternalLink(e){try{(0,a.postEvent)("web_app_open_tg_link",{path_full:e});return}catch(t){console.warn("[tvm-app-adapter] Telegram openInternalLink failed:",t)}await super.openInternalLink(e)}enableDebug(e){try{e?c.closingBehavior.enableConfirmation():c.closingBehavior.disableConfirmation()}catch{}}hapticsAvailable(){return V(a.hapticFeedback.selectionChanged)}async supports(e){switch(e){case"haptics":return this.hapticsAvailable();case"popup":return V(a.popup.show);case"qrScanner":return V(a.qrScanner.open);case"closeApp":return V(a.miniApp.close);case"backButton":return a.backButton.isSupported();case"backButtonVisibility":return a.backButton.hide.isSupported();case"bindCssVariables":return!0;case"openExternalLink":return V(a.openLink);case"openInternalLink":return!0;case"requestFullscreen":return!!(typeof c.viewport.requestFullscreen=="function"||a.viewport.requestFullscreen?.isAvailable?.());case"verticalSwipes":return!!(c.swipeBehavior.enableVertical.isAvailable()||c.swipeBehavior.disableVertical.isAvailable());case"viewVisibility":return!0;case"shareUrl":return typeof c.shareURL=="function";case"shareStory":return typeof c.shareStory=="function";case"copyTextToClipboard":return typeof c.copyTextToClipboard=="function";case"downloadFile":return typeof c.downloadFile=="function";case"addToHomeScreen":return typeof c.addToHomeScreen?.isAvailable=="function"?c.addToHomeScreen.isAvailable():typeof c.addToHomeScreen=="function";case"checkHomeScreenStatus":return typeof c.checkHomeScreenStatus=="function";case"requestPhone":return V(c.requestContact);default:return!1}}bindCssVariables(e){if(!this.cssVariablesBound)try{a.themeParams.bindCssVars(e),this.cssVariablesBound=!0}catch(t){if(t instanceof Error&&/css variables are already bound/i.test(t.message)){this.cssVariablesBound=!0;return}throw t}}vibrateImpact(e){this.hapticsAvailable()&&a.hapticFeedback.impactOccurred(e)}vibrateNotification(e){this.hapticsAvailable()&&a.hapticFeedback.notificationOccurred(e)}vibrateSelection(){this.hapticsAvailable()&&a.hapticFeedback.selectionChanged()}async showPopup(e){let t=x(a.popup.show,{title:e.title,message:e.message,buttons:e.buttons?.map(o=>({id:o.id,text:o.text??o.id,type:o.type??"default"}))});return t.ok?await t.value??null:super.showPopup(e)}async scanQRCode(e){let t=null,r=e?.closeOnCapture??!0,o=x(a.qrScanner.open,{onCaptured:p=>{t=p,r&&x(a.qrScanner.close)}});return o.ok?(await o.value,t):super.scanQRCode(e)}async closeApp(){x(a.miniApp.close).ok||await super.closeApp()}getInitData(){try{return a.initData.raw()}catch{return}}getLaunchParams(){let e=this.readCustomUrlParams(r=>r.toLowerCase().startsWith("tgwebapp")),t={};try{let r=(0,a.retrieveLaunchParams)(),o=r.tgWebAppStartParam;return typeof o=="string"&&o&&(t=this.normalizeDecodedStartParam(o)),{launchParams:r,customLaunchParams:{...e,...t}}}catch{return{customLaunchParams:{...e,...t}}}}decodeStartParam(e){try{return(0,c.decodeStartParam)(e)}catch{return}}requestFullscreen(){this.requestFullscreenInternal()}getViewportInsets(){try{let e=a.viewport.safeAreaInsets(),t=a.viewport.contentSafeAreaInsets();return{safeArea:e,contentSafeArea:t}}catch{return}}onViewportChange(e){let t=[],r=()=>typeof window<"u"?window.visualViewport?.height??window.innerHeight:0,o=l=>{let s=l?.height??this.safeHeightFromSdk(),u=l?.stableHeight??this.stableHeightFromSdk(),v=Number.isFinite(s)?s:r(),m=Number.isFinite(u)&&u>0?u:v;e({height:v,stableHeight:m})};(async()=>{try{await ne(this.getViewportMountOptions())}catch(l){console.warn("[tvm-app-adapter] ensureViewportMounted failed:",l)}})().finally(()=>o());let{sdkViewport:d}=this.getViewportMountOptions();if(typeof d.on=="function")try{let l=d.on("change",s=>o(s));typeof l=="function"&&t.push(l)}catch(l){console.warn("[tvm-app-adapter] viewport.on(change) subscription failed:",l)}try{typeof d.height?.sub=="function"&&t.push(d.height.sub(()=>o())),typeof d.stableHeight?.sub=="function"&&t.push(d.stableHeight.sub(()=>o()))}catch(l){console.warn("[tvm-app-adapter] viewport signal subscriptions failed:",l)}if(typeof window<"u"){let l=()=>o();window.visualViewport?.addEventListener("resize",l),window.addEventListener("resize",l),t.push(()=>{window.visualViewport?.removeEventListener("resize",l),window.removeEventListener("resize",l)})}return()=>{t.forEach(l=>{try{l()}catch{}})}}onAppearanceChange(e){return this.appearanceListeners.add(e),e(this.environment.appearance),()=>{this.appearanceListeners.delete(e)}}setBackButtonVisibility(e){a.backButton.isSupported()&&(e?a.backButton.show():a.backButton.hide())}enableVerticalSwipes(){try{let e=c.swipeBehavior;typeof e.isSupported=="function"&&e.isSupported()?(typeof e.isMounted=="function"&&!e.isMounted()&&e.mount?.(),e.enableVertical?.()):c.swipeBehavior.enableVertical.isAvailable()&&c.swipeBehavior.enableVertical()}catch(e){console.warn("[tvm-app-adapter] enableVerticalSwipes failed:",e)}}disableVerticalSwipes(){try{let e=c.swipeBehavior;typeof e.isSupported=="function"&&e.isSupported()?(typeof e.isMounted=="function"&&!e.isMounted()&&e.mount?.(),e.disableVertical?.()):c.swipeBehavior.disableVertical.isAvailable()&&c.swipeBehavior.disableVertical()}catch(e){console.warn("[tvm-app-adapter] disableVerticalSwipes failed:",e)}}onViewHide(e){return this.viewHideListeners.add(e),()=>{this.viewHideListeners.delete(e)}}onViewRestore(e){return this.viewRestoreListeners.add(e),()=>{this.viewRestoreListeners.delete(e)}}shareUrl(e,t){return(0,c.shareURL)(e,t)}async downloadFile(e,t){let r=x(c.downloadFile,e,t);if(r.ok)try{await r.value;return}catch(o){console.warn("[tvm-app-adapter] Telegram downloadFile failed:",o)}await super.downloadFile(e,t)}async shareStory(e,t){let r=t?.telegram?.text??t?.text,o=t?.telegram?.widgetLink??(t?.link?{url:t.link.url,...t.link.name?{name:t.link.name}:{}}:void 0);(0,c.shareStory)(e,{...r?{text:r}:{},...o?{widgetLink:o}:{}})}async addToHomeScreen(){return(typeof c.addToHomeScreen?.isAvailable=="function"?c.addToHomeScreen.isAvailable():!0)?new Promise(t=>{let r=()=>{(0,c.off)("home_screen_added",o),(0,c.off)("home_screen_failed",p)},o=()=>{r(),t(!0)},p=()=>{r(),t(!1)};(0,c.on)("home_screen_added",o),(0,c.on)("home_screen_failed",p);try{(0,c.addToHomeScreen)()}catch(d){r(),console.warn("[tvm-app-adapter] Telegram addToHomeScreen failed:",d),t(!1)}}):super.addToHomeScreen()}async checkHomeScreenStatus(){try{let e=await(0,c.checkHomeScreenStatus)();return typeof e=="string"?e:typeof e=="boolean"?e?"added":"not_added":"unknown"}catch(e){return console.warn("[tvm-app-adapter] Telegram checkHomeScreenStatus failed:",e),"unknown"}}async requestPhone(){if(!V(c.requestContact))return super.requestPhone();try{let e=await(0,c.requestContact)();if(!e||typeof e!="object")return null;let t=e.contact,r=t?.phoneNumber??t?.phone_number??t?.phone??e.phoneNumber??e.phone_number??e.phone;return typeof r=="string"&&r?r:null}catch(e){return console.warn("[tvm-app-adapter] Telegram requestPhone failed:",e),null}}setupAppearanceWatcher(){if(this.appearanceWatcherDispose?.(),typeof a.themeParams.isDark?.sub=="function"){let e=a.themeParams.isDark.sub(()=>{let t=a.themeParams.isDark()?"dark":"light";this.environment.appearance=t,this.notifyAppearance(t)});this.appearanceWatcherDispose=this.registerDisposable(e)}}notifyAppearance(e){for(let t of this.appearanceListeners)t(e)}setupActiveWatcher(){this.activeWatcherDispose?.();let e=a.miniApp.isActive,t=()=>{try{a.miniApp.isActive()?this.notifyViewRestore():this.notifyViewHide()}catch(r){console.warn("[tvm-app-adapter] miniApp.isActive() failed:",r)}};if(typeof e?.sub=="function"){let r=e.sub(()=>t());this.activeWatcherDispose=this.registerDisposable(r),t();return}try{t()}catch{}}async prepareViewport(){try{await He({...this.getViewportMountOptions(),bindCssVars:typeof a.viewport.bindCssVars=="function"?a.viewport.bindCssVars:void 0})}catch(e){console.warn("[tvm-app-adapter] prepareViewport failed:",e)}}async requestFullscreenInternal(){try{let e=this.getViewportMountOptions();await ne(e);let{sdkViewport:t}=e;(typeof t.isSupported=="function"?t.isSupported():!1)&&typeof t.requestFullscreen=="function"?await t.requestFullscreen():a.viewport.requestFullscreen&&a.viewport.requestFullscreen.isAvailable?.()?await a.viewport.requestFullscreen():(0,a.postEvent)("web_app_request_fullscreen"),this.disableVerticalSwipes()}catch(e){console.warn("[tvm-app-adapter] Telegram requestFullscreen failed:",e)}}getViewportMountOptions(){return{sdkViewport:c.viewport,fallbackMount:async()=>{a.viewport.mount?.isAvailable?.()&&await a.viewport.mount()}}}safeHeightFromSdk(){try{if(typeof c.viewport.height=="function")return c.viewport.height()}catch{return}}stableHeightFromSdk(){try{if(typeof c.viewport.stableHeight=="function")return c.viewport.stableHeight()}catch{return}}notifyViewHide(){for(let e of this.viewHideListeners)try{e()}catch(t){console.warn("[tvm-app-adapter] onViewHide listener failed:",t)}}normalizeDecodedStartParam(e){let t;try{t=(0,c.decodeStartParam)(e)}catch{t=e}if(t&&typeof t=="object"&&!Array.isArray(t))return{...t};if(typeof t=="string"&&t){let r=this.parseQueryString(t);return Object.keys(r).length?r:{startParam:t}}return{}}parseQueryString(e){let t=e.startsWith("?")?e.slice(1):e,r=new URLSearchParams(t),o={},p=new Set;for(let[d]of r.entries())p.add(d);for(let d of p){let l=r.getAll(d);l.length&&(o[d]=l.length===1?l[0]:l)}return o}notifyViewRestore(){for(let e of this.viewRestoreListeners)try{e()}catch(t){console.warn("[tvm-app-adapter] onViewRestore listener failed:",t)}}onDestroy(){this.appearanceWatcherDispose?.(),this.appearanceWatcherDispose=void 0,this.activeWatcherDispose?.(),this.activeWatcherDispose=void 0,this.appearanceListeners.clear(),this.viewHideListeners.clear(),this.viewRestoreListeners.clear(),this.backHandlers.clear(),super.onDestroy()}};var f=se(require("@vkontakte/vk-bridge"),1);var we=null;function re(i){if(typeof i!="string"){we=null;return}we=i.trim()||null}function be(){return we}async function Ne(i,n){if(typeof n!="function")return!1;try{return await n(i)}catch(e){return console.warn("[tvm-app-adapter] bridge.supportsAsync failed:",e),!1}}var St=/^[a-z0-9][a-z0-9_.:-]{0,63}$/i,De="VK_ANALYTICS_EVENT",I=class extends S{constructor(){super("vk");h(this,"configSafeArea");h(this,"stopViewportTracking");h(this,"supportsAsync",typeof f.default.supportsAsync=="function"?f.default.supportsAsync.bind(f.default):void 0);h(this,"pixelCodeWarningShown",!1);h(this,"unsubscribe");h(this,"launchParams");h(this,"queryParams");h(this,"viewHideListeners",new Set);h(this,"viewRestoreListeners",new Set)}computeSafeArea(){let e=this.computeBaseSafeArea(),t=this.resolveOverlayInsets();return t?U({environment:e,minimum:t}):e}async init(e){if(this.ready)return;let t=s=>this.handleBridgeEvent(s);f.default.subscribe(t),this.unsubscribe=this.registerDisposable(()=>f.default.unsubscribe(t));let r;try{r=await f.default.send("VKWebAppGetConfig")}catch(s){console.warn("[tvm-app-adapter] VKWebAppGetConfig failed:",s)}try{let s=await f.default.send("VKWebAppInit");s&&"result"in s&&s.result===!1&&console.warn("[tvm-app-adapter] VKWebAppInit returned result=false.")}catch(s){console.warn("[tvm-app-adapter] VKWebAppInit failed:",s)}let o;try{o=await f.default.send("VKWebAppGetLaunchParams")}catch(s){console.warn("[tvm-app-adapter] VKWebAppGetLaunchParams failed; falling back to URL params:",s)}let p=typeof window<"u"?window.location.search:"",d=(0,f.parseURLSearchParamsForGetLaunchParams)(p);this.launchParams=o,this.queryParams=d,this.environment=this.composeEnvironment(o,d,r),this.configSafeArea=this.environment.safeArea;let l=this.computeSafeArea();this.environment.safeArea=l,this.applyAppearance(this.environment.appearance,r?.scheme),this.notifyEnvironmentChanged(),this.ready=!0,this.startViewportTracking()}async vibrateImpact(e){await this.supportsBridgeMethod("VKWebAppTapticImpactOccurred")&&f.default.send("VKWebAppTapticImpactOccurred",{style:e})}async vibrateNotification(e){await this.supportsBridgeMethod("VKWebAppTapticNotificationOccurred")&&f.default.send("VKWebAppTapticNotificationOccurred",{type:e})}async vibrateSelection(){await this.supportsBridgeMethod("VKWebAppTapticSelectionChanged")&&f.default.send("VKWebAppTapticSelectionChanged")}async setColors(e){let{header:t,background:r}=e;if((t||r)&&await this.supportsBridgeMethod("VKWebAppSetViewSettings")){let p=t?this.resolveStatusBarStyle(t):this.environment.appearance?.includes("dark")?"light":"dark";await f.default.send("VKWebAppSetViewSettings",{status_bar_style:p,...t?{action_bar_color:t}:{},...r?{navigation_bar_color:r}:{}})}await super.setColors(e)}getEnvironment(){return{...this.environment,isWebView:f.default.isWebView()}}getLaunchParams(){return this.launchParams?{launchParams:this.launchParams,customLaunchParams:this.readCustomUrlParams(e=>{let t=e.toLowerCase();return t.startsWith("vk_")||t==="sign"})}:{customLaunchParams:this.readCustomUrlParams(e=>{let t=e.toLowerCase();return t.startsWith("vk_")||t==="sign"})}}async openExternalLink(e){let t=document.createElement("a");t.href=e,t.target="_blank",t.rel="noopener noreferrer",t.style.display="none",document.body.appendChild(t),t.click(),t.remove()}async closeApp(){if(!await this.supportsBridgeMethod("VKWebAppClose")){await super.closeApp();return}try{await f.default.send("VKWebAppClose",{status:"success"})}catch(t){console.warn("[tvm-app-adapter] VK closeApp failed:",t),await super.closeApp()}}async supports(e){switch(e){case"haptics":{let[t,r,o]=await Promise.all([this.supportsBridgeMethod("VKWebAppTapticImpactOccurred"),this.supportsBridgeMethod("VKWebAppTapticNotificationOccurred"),this.supportsBridgeMethod("VKWebAppTapticSelectionChanged")]);return t||r||o}case"qrScanner":return this.supportsBridgeMethod("VKWebAppOpenCodeReader");case"requestPhone":{let[t,r]=await Promise.all([this.supportsBridgeMethod("VKWebAppGetPhoneNumber"),this.supportsBridgeMethod("VKWebAppGetPersonalCard")]);return t||r}case"notifications":return this.supportsBridgeMethod("VKWebAppAllowNotifications");case"shareUrl":return this.supportsBridgeMethod("VKWebAppShare");case"shareStory":return this.supportsBridgeMethod("VKWebAppShowStoryBox");case"downloadFile":return this.supportsBridgeMethod("VKWebAppDownloadFile");case"addToHomeScreen":return this.supportsBridgeMethod("VKWebAppAddToHomeScreen");case"denyNotifications":return this.supportsBridgeMethod("VKWebAppDenyNotifications");case"closeApp":return this.supportsBridgeMethod("VKWebAppClose");case"openExternalLink":return!0;case"viewVisibility":return!0;default:return await super.supports(e)}}async requestPhone(){let[e,t]=await Promise.all([this.supportsBridgeMethod("VKWebAppGetPhoneNumber"),this.supportsBridgeMethod("VKWebAppGetPersonalCard")]);if(!e&&!t)return super.requestPhone();try{if(e){let d=(await f.default.send("VKWebAppGetPhoneNumber")).phone_number;return typeof d=="string"&&d?d:null}let o=(await f.default.send("VKWebAppGetPersonalCard",{type:["phone"]})).phone;return typeof o=="string"&&o?o:null}catch(r){return console.warn("[tvm-app-adapter] VK requestPhone failed:",r),null}}async requestNotificationsPermission(){if(!await this.supportsBridgeMethod("VKWebAppAllowNotifications"))return super.requestNotificationsPermission();try{let t=await f.default.send("VKWebAppAllowNotifications");return t&&typeof t=="object"&&"result"in t?!!t.result:!0}catch(t){return console.warn("[tvm-app-adapter] VK allow notifications failed:",t),!1}}async addToHomeScreen(){if(!await this.supportsBridgeMethod("VKWebAppAddToHomeScreen"))return console.warn("[tvm-app-adapter] VK addToHomeScreen not supported"),super.addToHomeScreen();try{let t=await f.default.send("VKWebAppAddToHomeScreen");return t&&typeof t=="object"&&"result"in t?!!t.result:!0}catch(t){return console.warn("[tvm-app-adapter] VK addToHomeScreen failed:",t),!1}}async denyNotifications(){if(!await this.supportsBridgeMethod("VKWebAppDenyNotifications"))return super.denyNotifications();try{let t=await f.default.send("VKWebAppDenyNotifications");return t&&typeof t=="object"&&"result"in t?!!t.result:!0}catch(t){return console.warn("[tvm-app-adapter] VK deny notifications failed:",t),!1}}async scanQRCode(e){if(!await this.supportsBridgeMethod("VKWebAppOpenCodeReader"))return super.scanQRCode(e);try{let r=await f.default.send("VKWebAppOpenCodeReader");return r.code_data?r.code_data:null}catch(r){return console.warn("[tvm-app-adapter] VK scanQRCode failed:",r),super.scanQRCode(e)}}onViewHide(e){return this.viewHideListeners.add(e),()=>{this.viewHideListeners.delete(e)}}onViewRestore(e){return this.viewRestoreListeners.add(e),()=>{this.viewRestoreListeners.delete(e)}}async shareStory(e,t){let r=t,o=r?.vk,p=r?.link?{type:"url",text:"open",url:r.link.url}:void 0,d=r?.text?[{sticker_type:"native",sticker:{action_type:"text",action:{text:r.text,style:"classic",background_style:"none"},transform:{gravity:"center_bottom",translation_y:-.2}}}]:void 0,l={background_type:o?.backgroundType??"image",url:e,locked:o?.locked??!0,...o?.attachment??p?{attachment:o?.attachment??p}:{},...o?.stickers??d?{stickers:o?.stickers??d}:{}};await f.default.send("VKWebAppShowStoryBox",l)}shareUrl(e,t){this.shareUrlInternal(e,t)}async shareUrlInternal(e,t){if(!await this.supportsBridgeMethod("VKWebAppShare")){super.shareUrl(e,t??"");return}try{await f.default.send("VKWebAppShare",{link:e,...t?{text:t}:{}})}catch(o){console.warn("[tvm-app-adapter] VK shareUrl failed:",o),super.shareUrl(e,t??"")}}async downloadFile(e,t){if(!await this.supportsBridgeMethod("VKWebAppDownloadFile")){await super.downloadFile(e,t);return}try{if((await f.default.send("VKWebAppDownloadFile",{url:e,filename:t}))?.result===!1)throw new Error("VKWebAppDownloadFile returned result=false")}catch(o){console.warn("[tvm-app-adapter] VK downloadFile failed:",o),await super.downloadFile(e,t)}}trackConversionEvent(e,t){let r=this.normalizeAnalyticsEventName(e);if(!r)return;let o={method:"VKWebAppConversionHit",params:{event:r,params:this.normalizeAnalyticsPayload(t)}};this.dispatchAnalytics(o)}trackPixelEvent(e,t){let r=be();if(!r){this.pixelCodeWarningShown||(console.warn("[VKAnalytics] VK pixel code is not configured. Call configureVkPixel() before tracking."),this.pixelCodeWarningShown=!0);return}let o=this.normalizeAnalyticsEventName(e);if(!o)return;let p={method:"VKWebAppRetargetingPixel",params:{pixel_code:r,type:o,data:this.normalizeAnalyticsPayload(t)}};this.pixelCodeWarningShown=!1,this.dispatchAnalytics(p)}dispatchAnalytics(e){if(typeof f.default.isWebView=="function")try{if(!f.default.isWebView()){this.emitAnalyticsFallback(e);return}}catch(t){console.warn("[VKAnalytics] bridge.isWebView check failed:",t),this.emitAnalyticsFallback(e);return}this.safeBridgeSend(e.method,e.params)}emitAnalyticsFallback(e){if(typeof window>"u")return;let t={...e,timestamp:Date.now()};if(typeof window.dispatchEvent=="function"&&typeof window.CustomEvent=="function"){window.dispatchEvent(new CustomEvent(De,{detail:t}));return}try{window.postMessage({type:De,detail:t},"*")}catch(r){console.warn("[VKAnalytics] fallback dispatch failed",r)}}normalizeAnalyticsEventName(e){if(typeof e!="string")return null;let t=e.trim();return!t||!St.test(t)?(console.warn(`[VKAnalytics] Invalid event name: "${e}"`),null):t}normalizeAnalyticsPayload(e){return this.isPlainObject(e)?{...e}:{}}isPlainObject(e){if(e===null||typeof e!="object"||Array.isArray(e))return!1;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null}async safeBridgeSend(e,t){try{if(!await this.supportsBridgeMethod(e))return;await f.default.send(e,t)}catch(r){console.warn(`[VKAnalytics] ${e} failed`,r)}}composeEnvironment(e,t,r){let o=t.vk_language??e?.vk_language,p=t.vk_platform??e?.vk_platform,d=t.vk_app_id??e?.vk_app_id,l=typeof window<"u"&&typeof window.matchMedia=="function"?window.matchMedia("(prefers-color-scheme: dark)").matches:!1,s=this.normalizeAppearance(r?.appearance,r?.scheme)??(l?"dark":"light"),u=this.extractSafeAreaFromConfig(r);return{platform:"vk",sdkVersion:p?String(p):void 0,appVersion:typeof d=="number"?`vk-app-${d}`:void 0,languageCode:o?String(o):void 0,appearance:s,isWebView:f.default.isWebView(),safeArea:u}}handleBridgeEvent(e){let{type:t,data:r}=e.detail??{};if(t==="VKWebAppViewHide"){this.notifyVisibilityListeners(this.viewHideListeners);return}if(t==="VKWebAppViewRestore"){this.notifyVisibilityListeners(this.viewRestoreListeners);return}if(t==="VKWebAppUpdateConfig"&&r){let o=r;this.updateEnvironmentFromConfig(o)}}updateEnvironmentFromConfig(e){if(!this.environment)return;let t=this.normalizeAppearance(e.appearance,e.scheme),r=!1;t&&t!==this.environment.appearance&&(this.environment.appearance=t,r=!0);let o=this.environment.safeArea,p=this.extractSafeAreaFromConfig(e);p?(this.configSafeArea=p,this.environment.safeArea=p):(this.configSafeArea=void 0,this.environment.safeArea=void 0);let d=this.computeSafeArea()??{top:0,right:0,bottom:0,left:0},l=o;!l||l.top!==d.top||l.right!==d.right||l.bottom!==d.bottom||l.left!==d.left?(this.environment.safeArea=d,r=!0):this.environment.safeArea=o,this.applyAppearance(this.environment.appearance,e.scheme),r&&this.notifyEnvironmentChanged()}applyAppearance(e,t){if(!(typeof document>"u")&&(e&&(document.documentElement.dataset.vkAppearance=e,document.documentElement.classList.toggle("dark",e==="dark")),t&&(document.documentElement.dataset.vkScheme=t,!e))){let r=this.normalizeAppearance(void 0,t);document.documentElement.classList.toggle("dark",r==="dark")}}resolveStatusBarStyle(e){let t=e.replace("#",""),r=t.length===3?t.split("").map(s=>s+s).join(""):t.slice(0,6),o=parseInt(r.slice(0,2),16)/255,p=parseInt(r.slice(2,4),16)/255,d=parseInt(r.slice(4,6),16)/255;return .2126*o+.7152*p+.0722*d>.6?"dark":"light"}normalizeAppearance(e,t){let r=e?.toLowerCase();if(r==="dark"||r==="light")return r;let o=t?.toLowerCase();if(o)return o.includes("dark")||o.includes("space_gray")?"dark":"light"}extractSafeAreaFromConfig(e){let t=e&&"insets"in e?e.insets:void 0;if(!t)return;let{top:r=0,right:o=0,bottom:p=0,left:d=0}=t,l=[r,o,p,d].map(g=>typeof g=="number"?g:Number(g)||0);if(!l.some(g=>g!==0))return;let[u,v,m,A]=l;return{top:u,right:v,bottom:m,left:A}}notifyVisibilityListeners(e){for(let t of e)try{t()}catch(r){console.warn("[tvm-app-adapter] VK visibility listener failed:",r)}}computeBaseSafeArea(){return U({environment:this.configSafeArea,viewport:this.getViewportInsets?.(),css:ee()})}startViewportTracking(){this.stopViewportTracking?.();let e=Re({getSafeArea:()=>this.computeSafeArea(),onChange:t=>{this.environment.safeArea=t,this.notifyEnvironmentChanged()}});e&&(this.stopViewportTracking=this.registerDisposable(e))}resolveOverlayInsets(){if(typeof window>"u"||!f.default.isWebView()||!this.isLikelyMobilePlatform())return;let e=window.innerWidth||document.documentElement?.clientWidth||0;if(!e||e>880)return;let o=!!window.matchMedia?.("(orientation: landscape)")?.matches;return{top:o?48:56,right:o?72:88}}isLikelyMobilePlatform(){let e=(this.resolveLaunchParam("vk_platform")??"").toLowerCase(),t=(this.resolveLaunchParam("vk_viewer_device")??"").toLowerCase();if(this.resolveLaunchParam("vk_is_layer")==="1")return!1;let o=/(iphone|ipad|ios|android|mobile)/i,p=/(desktop|web|tablet)/i,d=o.test(e)||o.test(t),l=p.test(e)||p.test(t);return d?!l:!1}resolveLaunchParam(e){let t=this.queryParams?.[e];if(typeof t=="string"&&t)return t;let r=this.launchParams?this.launchParams[e]:void 0;if(typeof r=="string"&&r)return r;if(typeof r=="number")return String(r);if(typeof r=="boolean")return r?"1":"0"}supportsBridgeMethod(e){return Ne(e,this.supportsAsync)}onDestroy(){this.unsubscribe?.(),this.unsubscribe=void 0,this.stopViewportTracking?.(),this.stopViewportTracking=void 0,this.viewHideListeners.clear(),this.viewRestoreListeners.clear(),super.onDestroy()}};var Ke=se(require("jsqr"),1);var F=class extends S{constructor(){super("web",{sdkVersion:navigator.userAgent,languageCode:navigator.language,isWebView:!1});h(this,"deferredPrompt",null);typeof window<"u"&&window.addEventListener("beforeinstallprompt",e=>{e.preventDefault(),this.deferredPrompt=e})}async supports(e){switch(e){case"copyTextToClipboard":return typeof navigator<"u"&&!!navigator.clipboard?.writeText;case"downloadFile":return typeof document<"u";case"shareUrl":return typeof navigator<"u"&&(!!navigator.share||!!navigator.clipboard?.writeText);case"addToHomeScreen":return/android/i.test(navigator.userAgent)&&!!this.deferredPrompt;case"checkHomeScreenStatus":return!0;default:return super.supports(e)}}async downloadFile(e,t){try{await X(e,t,{preferBlob:!0})}catch(r){console.warn("[mini-app-template] Web downloadFile fallback:",r),await super.downloadFile(e,t)}}scanQRCode(e){return new Promise(async t=>{if(typeof document>"u"){t(null);return}let r=document.body.style.overflow,o=document.body.style.position,p=document.body.style.touchAction,d=document.body.style.width,l=document.documentElement.style.overflow;document.body.style.overflow="hidden",document.body.style.position="fixed",document.body.style.width="100%",document.body.style.touchAction="none",document.documentElement.style.overflow="hidden";let s=document.createElement("div");s.id="qr-overlay",s.style.position="fixed",s.style.top="0",s.style.left="0",s.style.right="0",s.style.bottom="0",s.style.zIndex="999999999",s.style.background="rgba(0,0,0,0.92)",s.style.width="100%",s.style.height="100%",s.style.display="flex",s.style.flexDirection="column",s.style.alignItems="center",s.style.justifyContent="center",s.style.gap="20px",s.style.padding="24px",s.style.boxSizing="border-box",s.style.overflow="hidden",s.style.backdropFilter="blur(3px)",document.body.appendChild(s);let u=document.createElement("button");u.innerText="\u2715",u.style.position="absolute",u.style.top="22px",u.style.right="22px",u.style.fontSize="32px",u.style.color="white",u.style.background="transparent",u.style.border="none",u.style.cursor="pointer",u.style.zIndex="9999999999",s.appendChild(u);let v=Math.min(Math.floor(Math.min(window.innerWidth,window.innerHeight)*.72),320),m=document.createElement("div");m.style.width=`${v}px`,m.style.height=`${v}px`,m.style.position="relative",m.style.flex="0 0 auto",m.style.borderRadius="18px",m.style.overflow="hidden",s.appendChild(m);let A=document.createElement("div");A.style.position="absolute",A.style.inset="0",A.style.zIndex="1",A.style.background="#000",m.appendChild(A);let g=document.createElement("video");g.setAttribute("playsinline","true"),g.autoplay=!0,g.muted=!0,g.style.width="100%",g.style.height="100%",g.style.objectFit="cover",g.style.position="absolute",g.style.inset="0",A.appendChild(g);let w=document.createElement("div");w.style.position="absolute",w.style.top="0",w.style.left="0",w.style.right="0",w.style.bottom="0",w.style.border="3px solid rgba(255,255,255,0.9)",w.style.borderRadius="18px",w.style.pointerEvents="none",w.style.zIndex="3",m.appendChild(w);let y=document.createElement("div");y.style.position="absolute",y.style.left="0",y.style.right="0",y.style.height="2px",y.style.background="rgba(255,255,255,0.85)",y.style.borderRadius="2px",y.style.animation="qr-line 2s infinite",y.style.zIndex="4",m.appendChild(y);let M=document.createElement("style");M.innerHTML=`
2747
4
  @keyframes qr-line {
2748
5
  0% { top: 0; }
2749
6
  50% { top: calc(100% - 2px); }
2750
7
  100% { top: 0; }
2751
8
  }
2752
- `;
2753
- document.head.appendChild(styleTag);
2754
- const hint = document.createElement("div");
2755
- hint.innerText = "\u041D\u0430\u0432\u0435\u0434\u0438\u0442\u0435 \u043A\u0430\u043C\u0435\u0440\u0443 \u043D\u0430 QR-\u043A\u043E\u0434";
2756
- hint.style.color = "white";
2757
- hint.style.fontSize = "17px";
2758
- hint.style.opacity = "0.9";
2759
- overlay.appendChild(hint);
2760
- const bottomCloseBtn = document.createElement("button");
2761
- bottomCloseBtn.innerText = "\u0417\u0430\u043A\u0440\u044B\u0442\u044C";
2762
- bottomCloseBtn.style.minWidth = `${Math.min(scanSize, 220)}px`;
2763
- bottomCloseBtn.style.height = "48px";
2764
- bottomCloseBtn.style.padding = "0 20px";
2765
- bottomCloseBtn.style.border = "1px solid rgba(255,255,255,0.24)";
2766
- bottomCloseBtn.style.borderRadius = "14px";
2767
- bottomCloseBtn.style.background = "rgba(255,255,255,0.12)";
2768
- bottomCloseBtn.style.color = "white";
2769
- bottomCloseBtn.style.fontSize = "16px";
2770
- bottomCloseBtn.style.fontWeight = "600";
2771
- bottomCloseBtn.style.cursor = "pointer";
2772
- bottomCloseBtn.style.backdropFilter = "blur(8px)";
2773
- overlay.appendChild(bottomCloseBtn);
2774
- const canvas = document.createElement("canvas");
2775
- const context = canvas.getContext("2d", { willReadFrequently: true });
2776
- let stream = null;
2777
- let rafId = null;
2778
- let lastScanAt = 0;
2779
- let closed = false;
2780
- const finalize = async (result) => {
2781
- if (closed) {
2782
- return;
2783
- }
2784
- closed = true;
2785
- if (rafId !== null) {
2786
- cancelAnimationFrame(rafId);
2787
- rafId = null;
2788
- }
2789
- if (stream) {
2790
- stream.getTracks().forEach((track) => track.stop());
2791
- stream = null;
2792
- }
2793
- video.srcObject = null;
2794
- overlay.remove();
2795
- styleTag.remove();
2796
- document.body.style.overflow = prevOverflow;
2797
- document.body.style.position = prevPos;
2798
- document.body.style.width = prevWidth;
2799
- document.body.style.touchAction = prevTouch;
2800
- document.documentElement.style.overflow = prevHtmlOverflow;
2801
- resolve(result);
2802
- };
2803
- const removeFromBag = this.registerDisposable(() => {
2804
- void finalize(null);
2805
- });
2806
- const closeScanner = (result) => {
2807
- void finalize(result);
2808
- removeFromBag();
2809
- };
2810
- closeBtn.onclick = () => closeScanner(null);
2811
- bottomCloseBtn.onclick = () => closeScanner(null);
2812
- try {
2813
- const constraints = [
2814
- {
2815
- video: {
2816
- facingMode: { ideal: "environment" },
2817
- width: { ideal: 1920 },
2818
- height: { ideal: 1080 },
2819
- aspectRatio: { ideal: 1.7777777778 }
2820
- },
2821
- audio: false
2822
- },
2823
- {
2824
- video: {
2825
- facingMode: "environment",
2826
- width: { ideal: 1280 },
2827
- height: { ideal: 720 }
2828
- },
2829
- audio: false
2830
- },
2831
- { video: true, audio: false }
2832
- ];
2833
- let lastError = null;
2834
- for (const constraint of constraints) {
2835
- try {
2836
- stream = await navigator.mediaDevices.getUserMedia(constraint);
2837
- break;
2838
- } catch (error) {
2839
- lastError = error;
2840
- }
2841
- }
2842
- if (!stream) {
2843
- throw lastError instanceof Error ? lastError : new Error("Unable to access camera");
2844
- }
2845
- const [videoTrack] = stream.getVideoTracks();
2846
- if (videoTrack) {
2847
- try {
2848
- const advancedConstraints = [
2849
- { focusMode: "continuous" },
2850
- { exposureMode: "continuous" },
2851
- { whiteBalanceMode: "continuous" }
2852
- ];
2853
- await videoTrack.applyConstraints({
2854
- advanced: advancedConstraints
2855
- });
2856
- } catch {
2857
- }
2858
- }
2859
- video.srcObject = stream;
2860
- await video.play();
2861
- const scanFrame = (now) => {
2862
- if (closed) {
2863
- return;
2864
- }
2865
- if (now - lastScanAt >= 80 && context && video.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA) {
2866
- const width = video.videoWidth;
2867
- const height = video.videoHeight;
2868
- if (width > 0 && height > 0) {
2869
- canvas.width = width;
2870
- canvas.height = height;
2871
- context.drawImage(video, 0, 0, width, height);
2872
- const imageData = context.getImageData(0, 0, width, height);
2873
- const result = (0, import_jsqr.default)(imageData.data, width, height, {
2874
- inversionAttempts: "attemptBoth"
2875
- });
2876
- if (result?.data) {
2877
- closeScanner(result.data);
2878
- return;
2879
- }
2880
- lastScanAt = now;
2881
- }
2882
- }
2883
- rafId = requestAnimationFrame(scanFrame);
2884
- };
2885
- rafId = requestAnimationFrame(scanFrame);
2886
- } catch (error) {
2887
- console.error("QR Start error", error);
2888
- closeScanner(null);
2889
- }
2890
- });
2891
- }
2892
- shareUrl(url, text) {
2893
- if (navigator.share) {
2894
- try {
2895
- navigator.share({ title: text, text, url });
2896
- return;
2897
- } catch (err) {
2898
- console.warn("Share cancelled or failed:", err);
2899
- }
2900
- }
2901
- const payload = text ? `${text}
2902
- ${url}` : url;
2903
- this.copyTextToClipboard(payload).catch((err) => {
2904
- console.warn("Share fallback (clipboard) failed:", err);
2905
- });
2906
- }
2907
- async addToHomeScreen() {
2908
- const isAndroid = /android/i.test(navigator.userAgent);
2909
- if (!isAndroid || !this.deferredPrompt) {
2910
- return super.addToHomeScreen();
2911
- }
2912
- try {
2913
- this.deferredPrompt.prompt();
2914
- const choice = await this.deferredPrompt.userChoice;
2915
- this.deferredPrompt = null;
2916
- return choice?.outcome === "accepted";
2917
- } catch (error) {
2918
- console.warn("[tvm-app-adapter] Web addToHomeScreen failed:", error);
2919
- this.deferredPrompt = null;
2920
- return false;
2921
- }
2922
- }
2923
- async checkHomeScreenStatus() {
2924
- try {
2925
- const isStandalone = typeof window !== "undefined" && window.matchMedia?.("(display-mode: standalone)").matches || // iOS Safari specific flag
2926
- typeof navigator !== "undefined" && navigator.standalone === true;
2927
- if (isStandalone) {
2928
- return "added";
2929
- }
2930
- return "unknown";
2931
- } catch {
2932
- return "unknown";
2933
- }
2934
- }
2935
- };
2936
-
2937
- // src/adapters/index.ts
2938
- var CONFIRMED_PLATFORM_STORAGE_KEY = "mini-app-adapter:confirmed-platform";
2939
- var CONFIRMED_PLATFORM_TTL_MS = 30 * 60 * 1e3;
2940
- function detectPlatform() {
2941
- if (typeof window === "undefined") {
2942
- return "web";
2943
- }
2944
- const searchParams = new URLSearchParams(window.location.search);
2945
- const hashParams = (() => {
2946
- const hash = window.location.hash.startsWith("#") ? window.location.hash.slice(1) : window.location.hash;
2947
- return new URLSearchParams(hash);
2948
- })();
2949
- const getParam = (name) => searchParams.get(name) ?? hashParams.get(name);
2950
- const hasParam = (...names) => names.some((name) => getParam(name));
2951
- const readConfirmedPlatform = () => {
2952
- try {
2953
- const raw = window.sessionStorage.getItem(CONFIRMED_PLATFORM_STORAGE_KEY);
2954
- if (!raw) {
2955
- return null;
2956
- }
2957
- const parsed = JSON.parse(raw);
2958
- if (!parsed?.platform || typeof parsed.ts !== "number") {
2959
- return null;
2960
- }
2961
- if (Date.now() - parsed.ts > CONFIRMED_PLATFORM_TTL_MS) {
2962
- return null;
2963
- }
2964
- return parsed.platform;
2965
- } catch {
2966
- return null;
2967
- }
2968
- };
2969
- const persistConfirmedPlatform = (platform) => {
2970
- if (platform === "web") {
2971
- return;
2972
- }
2973
- try {
2974
- window.sessionStorage.setItem(
2975
- CONFIRMED_PLATFORM_STORAGE_KEY,
2976
- JSON.stringify({ platform, ts: Date.now() })
2977
- );
2978
- } catch {
2979
- }
2980
- };
2981
- const shellPlatform = readShellPlatform();
2982
- if (shellPlatform) {
2983
- persistConfirmedPlatform(shellPlatform);
2984
- return shellPlatform;
2985
- }
2986
- const userAgent = navigator.userAgent.toLowerCase();
2987
- const hasNativeBridge = typeof window.NativeBridge?.postMessage === "function";
2988
- if (hasNativeBridge) {
2989
- if (userAgent.includes("android")) {
2990
- persistConfirmedPlatform("shell_android");
2991
- return "shell_android";
2992
- }
2993
- persistConfirmedPlatform("shell_ios");
2994
- return "shell_ios";
2995
- }
2996
- const telegramGlobals = window;
2997
- const hasTelegramGlobal = Boolean(window.Telegram?.WebApp) || typeof telegramGlobals.TelegramWebviewProxy !== "undefined" || typeof telegramGlobals.TelegramGameProxy !== "undefined";
2998
- const hasTelegramParams = hasParam("tgWebAppPlatform", "tgWebAppVersion", "tgWebAppData", "tgWebAppLanguage");
2999
- if (hasTelegramGlobal || hasTelegramParams || userAgent.includes("telegram")) {
3000
- persistConfirmedPlatform("telegram");
3001
- return "telegram";
3002
- }
3003
- if (window.WebApp) {
3004
- persistConfirmedPlatform("max");
3005
- return "max";
3006
- }
3007
- if (window.MaxMiniApp) {
3008
- persistConfirmedPlatform("max");
3009
- return "max";
3010
- }
3011
- const hasVkParams = hasParam("vk_app_id", "vk_platform", "vk_user_id", "vk_language", "sign");
3012
- const hasVkUserAgentSignal = userAgent.includes("vkclient") || userAgent.includes("vk-android") || userAgent.includes("vkontakte");
3013
- if (hasVkParams || hasVkUserAgentSignal) {
3014
- persistConfirmedPlatform("vk");
3015
- return "vk";
3016
- }
3017
- const confirmedPlatform = readConfirmedPlatform();
3018
- if (confirmedPlatform && confirmedPlatform !== "web") {
3019
- return confirmedPlatform;
3020
- }
3021
- return "web";
3022
- }
3023
- function createAdapter(input) {
3024
- const options = normalizeCreateAdapterOptions(input);
3025
- const platform = options.platform ?? detectPlatform();
3026
- if (platform === "vk") {
3027
- setVkPixelCode(options.vk?.pixelCode ?? null);
3028
- }
3029
- switch (platform) {
3030
- case "shell_ios":
3031
- case "shell_android":
3032
- return new ShellMiniAppAdapter(platform);
3033
- case "telegram":
3034
- return new TelegramMiniAppAdapter();
3035
- case "vk":
3036
- return new VKMiniAppAdapter();
3037
- case "max":
3038
- return new MaxMiniAppAdapter();
3039
- default:
3040
- return new WebMiniAppAdapter();
3041
- }
3042
- }
3043
- function normalizeCreateAdapterOptions(input) {
3044
- if (!input) {
3045
- return {};
3046
- }
3047
- if (typeof input === "string") {
3048
- return { platform: input };
3049
- }
3050
- return input;
3051
- }
3052
-
3053
- // src/components/AdapterProvider.tsx
3054
- var import_react = require("react");
3055
-
3056
- // src/registry.ts
3057
- var currentAdapter = null;
3058
- function setActiveAdapter(adapter) {
3059
- currentAdapter = adapter;
3060
- }
3061
- function getActiveAdapter() {
3062
- return currentAdapter;
3063
- }
3064
-
3065
- // src/components/AdapterProvider.tsx
3066
- var import_jsx_runtime = require("react/jsx-runtime");
3067
- var AdapterContext = (0, import_react.createContext)(null);
3068
- function AdapterProvider({ adapter, children }) {
3069
- const proxiedAdapter = (0, import_react.useMemo)(() => {
3070
- return new Proxy(adapter, {
3071
- get(target, prop, receiver) {
3072
- const value = Reflect.get(target, prop, receiver);
3073
- if (typeof value === "function") {
3074
- return value.bind(target);
3075
- }
3076
- return value;
3077
- }
3078
- });
3079
- }, [adapter]);
3080
- (0, import_react.useEffect)(() => {
3081
- setActiveAdapter(proxiedAdapter);
3082
- return () => {
3083
- setActiveAdapter(null);
3084
- if (typeof proxiedAdapter.destroy === "function") {
3085
- try {
3086
- proxiedAdapter.destroy();
3087
- } catch (error) {
3088
- console.warn("[tvm-app-adapter] adapter destroy failed:", error);
3089
- }
3090
- }
3091
- };
3092
- }, [proxiedAdapter]);
3093
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AdapterContext.Provider, { value: proxiedAdapter, children });
3094
- }
3095
- function useMiniAppAdapter() {
3096
- const adapter = (0, import_react.useContext)(AdapterContext);
3097
- if (!adapter) {
3098
- throw new Error("useMiniAppAdapter must be used inside <AdapterProvider/>.");
3099
- }
3100
- return adapter;
3101
- }
3102
-
3103
- // src/hooks/useAdapterTheme.ts
3104
- var import_react2 = require("react");
3105
- var STORAGE_KEY = "loyalka-theme-preference";
3106
- function useAdapterTheme() {
3107
- const adapter = useMiniAppAdapter();
3108
- const [systemPrefersDark, setSystemPrefersDark] = (0, import_react2.useState)(() => {
3109
- const { appearance: appearance2 } = adapter.getEnvironment();
3110
- if (adapter.onAppearanceChange && appearance2) {
3111
- return appearance2 === "dark";
3112
- }
3113
- if (typeof window !== "undefined" && window.matchMedia) {
3114
- return window.matchMedia("(prefers-color-scheme: dark)").matches;
3115
- }
3116
- return false;
3117
- });
3118
- const [preference, setPreferenceState] = (0, import_react2.useState)(() => {
3119
- try {
3120
- const saved = localStorage.getItem(STORAGE_KEY);
3121
- if (saved === "light" || saved === "dark" || saved === "system") return saved;
3122
- } catch {
3123
- }
3124
- return "system";
3125
- });
3126
- (0, import_react2.useEffect)(() => {
3127
- if (adapter.onAppearanceChange) {
3128
- return adapter.onAppearanceChange((appearance2) => {
3129
- if (appearance2) {
3130
- setSystemPrefersDark(appearance2 === "dark");
3131
- }
3132
- });
3133
- }
3134
- if (typeof window !== "undefined" && window.matchMedia) {
3135
- const media = window.matchMedia("(prefers-color-scheme: dark)");
3136
- const handleChange = (event) => setSystemPrefersDark(event.matches);
3137
- media.addEventListener("change", handleChange);
3138
- return () => media.removeEventListener("change", handleChange);
3139
- }
3140
- return void 0;
3141
- }, [adapter]);
3142
- const isDark = (0, import_react2.useMemo)(() => {
3143
- if (preference === "dark" || preference === "system" && systemPrefersDark) {
3144
- return true;
3145
- }
3146
- return false;
3147
- }, [preference, systemPrefersDark]);
3148
- const appearance = isDark ? "dark" : "light";
3149
- (0, import_react2.useEffect)(() => {
3150
- const root = document.documentElement;
3151
- if (isDark) {
3152
- root.classList.add("dark");
3153
- } else {
3154
- root.classList.remove("dark");
3155
- }
3156
- }, [isDark]);
3157
- const setPreference = (0, import_react2.useCallback)((pref) => {
3158
- setPreferenceState(pref);
3159
- try {
3160
- localStorage.setItem(STORAGE_KEY, pref);
3161
- } catch {
3162
- }
3163
- }, []);
3164
- const toggle = (0, import_react2.useCallback)(() => {
3165
- document.documentElement.classList.add("theme-transition");
3166
- setTimeout(() => {
3167
- setTimeout(() => {
3168
- document.documentElement.classList.remove("theme-transition");
3169
- }, 400);
3170
- }, 50);
3171
- const root = document.documentElement;
3172
- const styles = getComputedStyle(root);
3173
- const primaryColor = styles.getPropertyValue("--primary").trim();
3174
- const backgroundColor = styles.getPropertyValue("--background").trim();
3175
- adapter.setColors({
3176
- header: primaryColor,
3177
- background: backgroundColor,
3178
- footer: backgroundColor
3179
- });
3180
- setPreference(preference === "dark" ? "light" : "dark");
3181
- }, [adapter, preference, setPreference]);
3182
- return {
3183
- isDark,
3184
- appearance,
3185
- preference,
3186
- setPreference,
3187
- toggle
3188
- };
3189
- }
3190
-
3191
- // src/hooks/useSafeArea.ts
3192
- var import_react3 = require("react");
3193
- var ZERO_SAFE_AREA = {
3194
- top: 0,
3195
- right: 0,
3196
- bottom: 0,
3197
- left: 0
3198
- };
3199
- function useSafeArea() {
3200
- const adapter = useMiniAppAdapter();
3201
- const computeSafeArea = (0, import_react3.useCallback)(
3202
- () => adapter.computeSafeArea() ?? ZERO_SAFE_AREA,
3203
- [adapter]
3204
- );
3205
- const [safeArea, setSafeArea] = (0, import_react3.useState)(computeSafeArea);
3206
- (0, import_react3.useEffect)(() => {
3207
- setSafeArea(computeSafeArea());
3208
- const unsubscribe = adapter.subscribe?.(() => {
3209
- setSafeArea(computeSafeArea());
3210
- });
3211
- return () => unsubscribe?.();
3212
- }, [adapter, computeSafeArea]);
3213
- return safeArea ?? ZERO_SAFE_AREA;
3214
- }
3215
-
3216
- // src/platform.ts
3217
- var cachedPlatform = null;
3218
- function getPlatform() {
3219
- const adapter = getActiveAdapter();
3220
- if (adapter) {
3221
- if (adapter.platform !== "web") {
3222
- cachedPlatform = adapter.platform;
3223
- }
3224
- return adapter.platform;
3225
- }
3226
- if (cachedPlatform && cachedPlatform !== "web") {
3227
- return cachedPlatform;
3228
- }
3229
- const detectedPlatform = detectPlatform();
3230
- if (detectedPlatform !== "web") {
3231
- cachedPlatform = detectedPlatform;
3232
- }
3233
- return detectedPlatform;
3234
- }
3235
-
3236
- // src/analytics.ts
3237
- function configureVkPixel(pixelCode2) {
3238
- setVkPixelCode(pixelCode2);
3239
- }
3240
- function trackConversionEvent(event, payload) {
3241
- const adapter = resolveVkAdapter();
3242
- adapter?.trackConversionEvent(event, payload);
3243
- }
3244
- function trackPixelEvent(event, payload) {
3245
- const adapter = resolveVkAdapter();
3246
- adapter?.trackPixelEvent(event, payload);
3247
- }
3248
- function resolveVkAdapter() {
3249
- const adapter = getActiveAdapter();
3250
- if (adapter) {
3251
- return adapter.platform === "vk" ? adapter : null;
3252
- }
3253
- if (getPlatform() !== "vk") {
3254
- return null;
3255
- }
3256
- return null;
3257
- }
3258
- // Annotate the CommonJS export names for ESM import in node:
3259
- 0 && (module.exports = {
3260
- AdapterProvider,
3261
- BaseMiniAppAdapter,
3262
- MaxMiniAppAdapter,
3263
- ShellMiniAppAdapter,
3264
- TelegramMiniAppAdapter,
3265
- VKMiniAppAdapter,
3266
- WebMiniAppAdapter,
3267
- configureVkPixel,
3268
- createAdapter,
3269
- createShellAPI,
3270
- detectPlatform,
3271
- getActiveAdapter,
3272
- getPlatform,
3273
- isShell,
3274
- isShellAndroid,
3275
- isShellIOS,
3276
- readShellPlatform,
3277
- requestShellPushPermission,
3278
- shell,
3279
- storeShellToken,
3280
- trackConversionEvent,
3281
- trackPixelEvent,
3282
- useAdapterTheme,
3283
- useMiniAppAdapter,
3284
- useSafeArea
3285
- });
3286
- //# sourceMappingURL=index.cjs.map
9
+ `,document.head.appendChild(M);let P=document.createElement("div");P.innerText="\u041D\u0430\u0432\u0435\u0434\u0438\u0442\u0435 \u043A\u0430\u043C\u0435\u0440\u0443 \u043D\u0430 QR-\u043A\u043E\u0434",P.style.color="white",P.style.fontSize="17px",P.style.opacity="0.9",s.appendChild(P);let b=document.createElement("button");b.innerText="\u0417\u0430\u043A\u0440\u044B\u0442\u044C",b.style.minWidth=`${Math.min(v,220)}px`,b.style.height="48px",b.style.padding="0 20px",b.style.border="1px solid rgba(255,255,255,0.24)",b.style.borderRadius="14px",b.style.background="rgba(255,255,255,0.12)",b.style.color="white",b.style.fontSize="16px",b.style.fontWeight="600",b.style.cursor="pointer",b.style.backdropFilter="blur(8px)",s.appendChild(b);let ie=document.createElement("canvas"),oe=ie.getContext("2d",{willReadFrequently:!0}),L=null,D=null,ke=0,ae=!1,Se=async T=>{ae||(ae=!0,D!==null&&(cancelAnimationFrame(D),D=null),L&&(L.getTracks().forEach(K=>K.stop()),L=null),g.srcObject=null,s.remove(),M.remove(),document.body.style.overflow=r,document.body.style.position=o,document.body.style.width=d,document.body.style.touchAction=p,document.documentElement.style.overflow=l,t(T))},it=this.registerDisposable(()=>{Se(null)}),J=T=>{Se(T),it()};u.onclick=()=>J(null),b.onclick=()=>J(null);try{let T=[{video:{facingMode:{ideal:"environment"},width:{ideal:1920},height:{ideal:1080},aspectRatio:{ideal:1.7777777778}},audio:!1},{video:{facingMode:"environment",width:{ideal:1280},height:{ideal:720}},audio:!1},{video:!0,audio:!1}],K=null;for(let R of T)try{L=await navigator.mediaDevices.getUserMedia(R);break}catch(B){K=B}if(!L)throw K instanceof Error?K:new Error("Unable to access camera");let[Pe]=L.getVideoTracks();if(Pe)try{let R=[{focusMode:"continuous"},{exposureMode:"continuous"},{whiteBalanceMode:"continuous"}];await Pe.applyConstraints({advanced:R})}catch{}g.srcObject=L,await g.play();let Ce=R=>{if(!ae){if(R-ke>=80&&oe&&g.readyState>=HTMLMediaElement.HAVE_CURRENT_DATA){let B=g.videoWidth,q=g.videoHeight;if(B>0&&q>0){ie.width=B,ie.height=q,oe.drawImage(g,0,0,B,q);let ot=oe.getImageData(0,0,B,q),Me=(0,Ke.default)(ot.data,B,q,{inversionAttempts:"attemptBoth"});if(Me?.data){J(Me.data);return}ke=R}}D=requestAnimationFrame(Ce)}};D=requestAnimationFrame(Ce)}catch(T){console.error("QR Start error",T),J(null)}})}shareUrl(e,t){if(navigator.share)try{navigator.share({title:t,text:t,url:e});return}catch(o){console.warn("Share cancelled or failed:",o)}let r=t?`${t}
10
+ ${e}`:e;this.copyTextToClipboard(r).catch(o=>{console.warn("Share fallback (clipboard) failed:",o)})}async addToHomeScreen(){if(!/android/i.test(navigator.userAgent)||!this.deferredPrompt)return super.addToHomeScreen();try{this.deferredPrompt.prompt();let t=await this.deferredPrompt.userChoice;return this.deferredPrompt=null,t?.outcome==="accepted"}catch(t){return console.warn("[tvm-app-adapter] Web addToHomeScreen failed:",t),this.deferredPrompt=null,!1}}async checkHomeScreenStatus(){try{return typeof window<"u"&&window.matchMedia?.("(display-mode: standalone)").matches||typeof navigator<"u"&&navigator.standalone===!0?"added":"unknown"}catch{return"unknown"}}};var qe="mini-app-adapter:confirmed-platform";function $(){if(typeof window>"u")return"web";let i=new URLSearchParams(window.location.search),n=(()=>{let y=window.location.hash.startsWith("#")?window.location.hash.slice(1):window.location.hash;return new URLSearchParams(y)})(),e=y=>i.get(y)??n.get(y),t=(...y)=>y.some(M=>e(M)),r=()=>{let y=[];try{window.localStorage&&y.push(window.localStorage)}catch{}try{window.sessionStorage&&y.push(window.sessionStorage)}catch{}return y},o=()=>{for(let y of r())try{let M=y.getItem(qe);if(!M)continue;let P=JSON.parse(M);if(P?.platform&&P.platform!=="web")return P.platform}catch{}return null},p=y=>{if(y==="web")return;let M=JSON.stringify({platform:y,ts:Date.now()});for(let P of r())try{P.setItem(qe,M)}catch{}},d=G();if(d)return p(d),d;let l=navigator.userAgent.toLowerCase();if(typeof window.NativeBridge?.postMessage=="function")return l.includes("android")?(p("shell_android"),"shell_android"):(p("shell_ios"),"shell_ios");let u=window,v=!!window.Telegram?.WebApp||typeof u.TelegramWebviewProxy<"u"||typeof u.TelegramGameProxy<"u",m=t("tgWebAppPlatform","tgWebAppVersion","tgWebAppData","tgWebAppLanguage");if(v||m||l.includes("telegram"))return p("telegram"),"telegram";let A=t("vk_app_id","vk_platform","vk_user_id","vk_language","sign"),g=l.includes("vkclient")||l.includes("vk-android")||l.includes("vkontakte");if(A||g)return p("vk"),"vk";let w=o();return w&&w!=="web"?w:"web"}function ze(i){let n=Pt(i),e=n.platform??$();switch(e==="vk"&&re(n.vk?.pixelCode??null),e){case"shell_ios":case"shell_android":return new _(e);case"telegram":return new W;case"vk":return new I;default:return new F}}function Pt(i){return i?typeof i=="string"?{platform:i}:i:{}}var E=require("react");var Qe=null;function Ae(i){Qe=i}function O(){return Qe}var je=require("react/jsx-runtime"),Ue=(0,E.createContext)(null);function Ge({adapter:i,children:n}){let e=(0,E.useMemo)(()=>{let t=new Map;return new Proxy(i,{get(r,o,p){let d=Reflect.get(r,o,p);if(typeof d!="function")return d;let l=t.get(o);return l||(l=d.bind(r),t.set(o,l)),l}})},[i]);return(0,E.useEffect)(()=>(Ae(e),()=>{Ae(null)}),[e]),(0,je.jsx)(Ue.Provider,{value:e,children:n})}function H(){let i=(0,E.useContext)(Ue);if(!i)throw new Error("useMiniAppAdapter must be used inside <AdapterProvider/>.");return i}var C=require("react");var $e="mini-app-adapter:theme-preference";function Ye(){let i=H(),[n,e]=(0,C.useState)(()=>{let{appearance:s}=i.getEnvironment();return i.onAppearanceChange&&s?s==="dark":typeof window<"u"&&window.matchMedia?window.matchMedia("(prefers-color-scheme: dark)").matches:!1}),[t,r]=(0,C.useState)(()=>{try{let s=localStorage.getItem($e);if(s==="light"||s==="dark"||s==="system")return s}catch{}return"system"});(0,C.useEffect)(()=>{if(i.onAppearanceChange)return i.onAppearanceChange(s=>{s&&e(s==="dark")});if(typeof window<"u"&&window.matchMedia){let s=window.matchMedia("(prefers-color-scheme: dark)"),u=v=>e(v.matches);return s.addEventListener("change",u),()=>s.removeEventListener("change",u)}},[i]);let o=(0,C.useMemo)(()=>!!(t==="dark"||t==="system"&&n),[t,n]),p=o?"dark":"light";(0,C.useEffect)(()=>{let s=document.documentElement;o?s.classList.add("dark"):s.classList.remove("dark");let u=getComputedStyle(s),v=u.getPropertyValue("--primary").trim(),m=u.getPropertyValue("--background").trim();(v||m)&&i.setColors({...v?{header:v}:{},...m?{background:m,footer:m}:{}})},[o,i]);let d=(0,C.useCallback)(s=>{r(s);try{localStorage.setItem($e,s)}catch{}},[]),l=(0,C.useCallback)(()=>{document.documentElement.classList.add("theme-transition"),setTimeout(()=>{setTimeout(()=>{document.documentElement.classList.remove("theme-transition")},400)},50),d(t==="dark"?"light":"dark")},[t,d]);return{isDark:o,appearance:p,preference:t,setPreference:d,toggle:l}}var N=require("react");var Je={top:0,right:0,bottom:0,left:0};function Ze(){let i=H(),n=(0,N.useCallback)(()=>i.computeSafeArea()??Je,[i]),[e,t]=(0,N.useState)(n);return(0,N.useEffect)(()=>{t(n());let r=i.subscribe?.(()=>{t(n())});return()=>r?.()},[i,n]),e??Je}function Xe(i){re(i)}function et(i,n){nt()?.trackConversionEvent(i,n)}function tt(i,n){nt()?.trackPixelEvent(i,n)}function nt(){let i=O();return i&&i.platform==="vk"?i:null}var Y=null;function rt(){let i=O();if(i)return i.platform!=="web"&&(Y=i.platform),i.platform;if(Y&&Y!=="web")return Y;let n=$();return n!=="web"&&(Y=n),n}0&&(module.exports={AdapterProvider,BaseMiniAppAdapter,ShellMiniAppAdapter,TelegramMiniAppAdapter,VKMiniAppAdapter,WebMiniAppAdapter,configureVkPixel,createAdapter,createShellAPI,detectPlatform,getActiveAdapter,getPlatform,isShell,isShellAndroid,isShellIOS,readShellPlatform,requestShellPushPermission,shell,storeShellToken,trackConversionEvent,trackPixelEvent,useAdapterTheme,useMiniAppAdapter,useSafeArea});