rampkit-expo-dev 0.0.83 → 0.0.85

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.
@@ -2,8 +2,8 @@ import { RampKitContext, NavigationData } from "./types";
2
2
  export declare const injectedHardening = "\n(function(){\n try {\n var meta = document.querySelector('meta[name=\"viewport\"]');\n if (!meta) { meta = document.createElement('meta'); meta.name = 'viewport'; document.head.appendChild(meta); }\n meta.setAttribute('content','width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover');\n var style = document.createElement('style');\n style.textContent='html,body{overflow-x:hidden!important;} html,body,*{-webkit-user-select:none!important;user-select:none!important;-webkit-touch-callout:none!important;-ms-user-select:none!important;touch-action: pan-y;} *{-webkit-tap-highlight-color: rgba(0,0,0,0)!important;} ::selection{background: transparent!important;} ::-moz-selection{background: transparent!important;} a,img{-webkit-user-drag:none!important;user-drag:none!important;-webkit-touch-callout:none!important} input,textarea{caret-color:transparent!important;-webkit-user-select:none!important;user-select:none!important}';\n document.head.appendChild(style);\n var prevent=function(e){e.preventDefault&&e.preventDefault();};\n document.addEventListener('gesturestart',prevent,{passive:false});\n document.addEventListener('gesturechange',prevent,{passive:false});\n document.addEventListener('gestureend',prevent,{passive:false});\n document.addEventListener('dblclick',prevent,{passive:false});\n document.addEventListener('wheel',function(e){ if(e.ctrlKey) e.preventDefault(); },{passive:false});\n document.addEventListener('touchmove',function(e){ if(e.scale && e.scale !== 1) e.preventDefault(); },{passive:false});\n document.addEventListener('selectstart',prevent,{passive:false,capture:true});\n document.addEventListener('contextmenu',prevent,{passive:false,capture:true});\n document.addEventListener('copy',prevent,{passive:false,capture:true});\n document.addEventListener('cut',prevent,{passive:false,capture:true});\n document.addEventListener('paste',prevent,{passive:false,capture:true});\n document.addEventListener('dragstart',prevent,{passive:false,capture:true});\n // Belt-and-suspenders: aggressively clear any attempted selection\n var clearSel=function(){\n try{var sel=window.getSelection&&window.getSelection(); if(sel&&sel.removeAllRanges) sel.removeAllRanges();}catch(_){} }\n document.addEventListener('selectionchange',clearSel,{passive:true,capture:true});\n document.onselectstart=function(){ clearSel(); return false; };\n try{ document.documentElement.style.webkitUserSelect='none'; document.documentElement.style.userSelect='none'; }catch(_){ }\n try{ document.body.style.webkitUserSelect='none'; document.body.style.userSelect='none'; }catch(_){ }\n var __selTimer = setInterval(clearSel, 160);\n window.addEventListener('pagehide',function(){ try{ clearInterval(__selTimer); }catch(_){} });\n // Continuously enforce no-select on all elements and new nodes\n var enforceNoSelect = function(el){\n try{\n el.style && (el.style.webkitUserSelect='none', el.style.userSelect='none', el.style.webkitTouchCallout='none');\n el.setAttribute && (el.setAttribute('unselectable','on'), el.setAttribute('contenteditable','false'));\n }catch(_){}\n }\n try{\n var all=document.getElementsByTagName('*');\n for(var i=0;i<all.length;i++){ enforceNoSelect(all[i]); }\n var obs = new MutationObserver(function(muts){\n for(var j=0;j<muts.length;j++){\n var m=muts[j];\n if(m.type==='childList'){\n m.addedNodes && m.addedNodes.forEach && m.addedNodes.forEach(function(n){ if(n && n.nodeType===1){ enforceNoSelect(n); var q=n.getElementsByTagName? n.getElementsByTagName('*'): []; for(var k=0;k<q.length;k++){ enforceNoSelect(q[k]); }}});\n } else if(m.type==='attributes'){\n enforceNoSelect(m.target);\n }\n }\n });\n obs.observe(document.documentElement,{ childList:true, subtree:true, attributes:true, attributeFilter:['contenteditable','style'] });\n }catch(_){ }\n } catch(_) {}\n})(); true;\n";
3
3
  export declare const injectedNoSelect = "\n(function(){\n try {\n if (window.__rkNoSelectApplied) return true;\n window.__rkNoSelectApplied = true;\n var style = document.getElementById('rk-no-select-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'rk-no-select-style';\n style.innerHTML = \"\n * {\n user-select: none !important;\n -webkit-user-select: none !important;\n -webkit-touch-callout: none !important;\n }\n ::selection {\n background: transparent !important;\n }\n \";\n document.head.appendChild(style);\n }\n var prevent = function(e){ if(e && e.preventDefault) e.preventDefault(); return false; };\n document.addEventListener('contextmenu', prevent, { passive: false, capture: true });\n document.addEventListener('selectstart', prevent, { passive: false, capture: true });\n } catch (_) {}\n true;\n})();\n";
4
4
  export declare const injectedVarsHandler = "\n(function(){\n try {\n if (window.__rkVarsHandlerApplied) return true;\n window.__rkVarsHandlerApplied = true;\n \n // Handler function that updates variables and notifies the page\n window.__rkHandleVarsUpdate = function(vars) {\n if (!vars || typeof vars !== 'object') return;\n // Update the global variables object\n window.__rampkitVariables = vars;\n // Dispatch a custom event that the page's JS can listen to for re-rendering\n try {\n document.dispatchEvent(new CustomEvent('rampkit:vars-updated', { detail: vars }));\n } catch(e) {}\n // Also try calling a global handler if the page defined one\n try {\n if (typeof window.onRampkitVarsUpdate === 'function') {\n window.onRampkitVarsUpdate(vars);\n }\n } catch(e) {}\n };\n \n // Listen for message events from React Native\n document.addEventListener('message', function(event) {\n try {\n var data = event.data;\n if (data && data.type === 'rampkit:variables' && data.vars) {\n window.__rkHandleVarsUpdate(data.vars);\n }\n } catch(e) {}\n }, false);\n \n // Also listen on window for compatibility\n window.addEventListener('message', function(event) {\n try {\n var data = event.data;\n if (data && data.type === 'rampkit:variables' && data.vars) {\n window.__rkHandleVarsUpdate(data.vars);\n }\n } catch(e) {}\n }, false);\n } catch (_) {}\n true;\n})();\n";
5
- export declare const injectedDynamicTapHandler = "\n(function() {\n if (window.__rampkitClickInterceptorInstalled) return;\n window.__rampkitClickInterceptorInstalled = true;\n\n // ========== DEBUG: phone-21 (buggy screen) specific logging (REMOVE AFTER BUG FIX) ==========\n var isBuggyScreen = function() {\n var screenId = window.__rampkitScreenId || '';\n var screenIndex = window.__rampkitScreenIndex;\n return screenId === 'phone-21' || screenIndex === 18;\n };\n\n var debugBuggyScreen = function(source, msg, data) {\n if (isBuggyScreen()) {\n var logData = { source: source, msg: msg, data: data };\n console.log('[BUGGY-SCREEN][' + source + '] ' + msg, JSON.stringify(data || {}));\n // Send to React Native via postMessage for visibility in RN console\n try {\n if (window.ReactNativeWebView && window.ReactNativeWebView.postMessage) {\n window.ReactNativeWebView.postMessage(JSON.stringify({\n type: 'rampkit:debug-buggy-screen',\n source: source,\n message: msg,\n data: data\n }));\n }\n } catch(e) {}\n }\n };\n // ========== END DEBUG ==========\n\n // Decode HTML entities\n function decodeHtml(str) {\n if (!str) return str;\n return str.replace(/&quot;/g, '\"').replace(/&#34;/g, '\"').replace(/&#x22;/g, '\"')\n .replace(/&apos;/g, \"'\").replace(/&#39;/g, \"'\").replace(/&#x27;/g, \"'\")\n .replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&amp;/g, '&');\n }\n\n // Find dynamic tap config on element or ancestors\n function findDynamicTap(el) {\n var current = el;\n var depth = 0;\n var attrNames = ['data-tap-dynamic', 'data-tapdynamic', 'tapDynamic', 'data-dynamic-tap'];\n var searchTrail = []; // DEBUG: Track search path\n\n while (current && current !== document.body && current !== document.documentElement && depth < 20) {\n // DEBUG: Build search trail for Screen 19\n var trailInfo = {\n depth: depth,\n tag: current.tagName,\n id: current.id || '(none)',\n class: (current.className && typeof current.className === 'string') ? current.className.substring(0,50) : '(none)'\n };\n searchTrail.push(trailInfo);\n\n if (current.getAttribute) {\n for (var i = 0; i < attrNames.length; i++) {\n var attr = current.getAttribute(attrNames[i]);\n if (attr && attr.length > 2) {\n var rect = current.getBoundingClientRect ? current.getBoundingClientRect() : {};\n debugBuggyScreen('DynamicTap', 'FOUND data-tap-dynamic', {\n attrName: attrNames[i],\n depth: depth,\n element: trailInfo,\n size: rect.width + 'x' + rect.height,\n configPreview: attr.substring(0, 100) + '...',\n trail: searchTrail\n });\n return { element: current, config: attr };\n }\n }\n if (current.dataset && current.dataset.tapDynamic) {\n var rect2 = current.getBoundingClientRect ? current.getBoundingClientRect() : {};\n debugBuggyScreen('DynamicTap', 'FOUND dataset.tapDynamic', {\n depth: depth,\n element: trailInfo,\n size: rect2.width + 'x' + rect2.height,\n trail: searchTrail\n });\n return { element: current, config: current.dataset.tapDynamic };\n }\n }\n current = current.parentElement;\n depth++;\n }\n debugBuggyScreen('DynamicTap', 'NO data-tap-dynamic found', { trail: searchTrail });\n return null;\n }\n \n // Get variables for condition evaluation - check ALL possible sources\n function getVars() {\n var vars = {};\n if (window.__rampkitVariables) {\n Object.keys(window.__rampkitVariables).forEach(function(k) {\n vars[k] = window.__rampkitVariables[k];\n });\n }\n if (window.__rampkitVars) {\n Object.keys(window.__rampkitVars).forEach(function(k) {\n vars[k] = window.__rampkitVars[k];\n });\n }\n if (window.RK_VARS) {\n Object.keys(window.RK_VARS).forEach(function(k) {\n vars[k] = window.RK_VARS[k];\n });\n }\n return vars;\n }\n \n // Evaluate a single rule\n function evalRule(rule, vars) {\n if (!rule || !rule.key) return false;\n var left = vars[rule.key];\n var right = rule.value;\n var op = rule.op || '=';\n if (left === undefined || left === null) left = '';\n if (right === undefined || right === null) right = '';\n var leftStr = String(left);\n var rightStr = String(right);\n var result = false;\n switch (op) {\n case '=': case '==': result = leftStr === rightStr; break;\n case '!=': case '<>': result = leftStr !== rightStr; break;\n case '>': result = parseFloat(left) > parseFloat(right); break;\n case '<': result = parseFloat(left) < parseFloat(right); break;\n case '>=': result = parseFloat(left) >= parseFloat(right); break;\n case '<=': result = parseFloat(left) <= parseFloat(right); break;\n default: result = false;\n }\n return result;\n }\n \n // Evaluate all rules (AND logic)\n function evalRules(rules, vars) {\n if (!rules || !rules.length) return true;\n for (var i = 0; i < rules.length; i++) {\n if (!evalRule(rules[i], vars)) return false;\n }\n return true;\n }\n \n // Execute an action\n function execAction(action) {\n if (!action || !action.type) return;\n var msg = null;\n var actionType = action.type.toLowerCase();\n \n switch (actionType) {\n case 'navigate':\n msg = { type: 'rampkit:navigate', targetScreenId: action.targetScreenId || '__continue__', animation: action.animation || 'fade' };\n break;\n case 'continue':\n msg = { type: 'rampkit:navigate', targetScreenId: '__continue__', animation: action.animation || 'fade' };\n break;\n case 'goback':\n msg = { type: 'rampkit:goBack', animation: action.animation || 'fade' };\n break;\n case 'close':\n msg = { type: 'rampkit:close' };\n break;\n case 'haptic':\n msg = { type: 'rampkit:haptic', hapticType: action.hapticType || 'impact', impactStyle: action.impactStyle || 'Medium', notificationType: action.notificationType };\n break;\n case 'showpaywall':\n msg = { type: 'rampkit:show-paywall', payload: action.payload || { paywallId: action.paywallId } };\n break;\n case 'requestreview':\n msg = { type: 'rampkit:request-review' };\n break;\n case 'requestnotificationpermission':\n msg = { type: 'rampkit:request-notification-permission' };\n break;\n case 'onboardingfinished':\n msg = { type: 'rampkit:onboarding-finished', payload: action.payload };\n break;\n case 'setvariable':\n case 'setstate':\n case 'updatevariable':\n case 'set':\n case 'assign':\n var varKey = action.key || action.variableName || action.name || action.variable;\n var varValue = action.variableValue !== undefined ? action.variableValue :\n action.value !== undefined ? action.value :\n action.newValue !== undefined ? action.newValue : undefined;\n if (varKey && varValue !== undefined) {\n if (window.__rampkitVariables) window.__rampkitVariables[varKey] = varValue;\n if (window.__rampkitVars) window.__rampkitVars[varKey] = varValue;\n var updateVars = {};\n updateVars[varKey] = varValue;\n msg = { type: 'rampkit:variables', vars: updateVars };\n }\n break;\n }\n if (msg) {\n try {\n if (window.ReactNativeWebView && window.ReactNativeWebView.postMessage) {\n window.ReactNativeWebView.postMessage(JSON.stringify(msg));\n }\n } catch(e) {}\n }\n }\n \n // Evaluate dynamic tap config\n function evalDynamicTap(config) {\n if (!config || !config.values) return false;\n var vars = getVars();\n var conditions = config.values;\n for (var i = 0; i < conditions.length; i++) {\n var cond = conditions[i];\n var condType = cond.conditionType || 'if';\n var rules = cond.rules || [];\n var actions = cond.actions || [];\n if (condType === 'else' || evalRules(rules, vars)) {\n for (var j = 0; j < actions.length; j++) {\n execAction(actions[j]);\n }\n return true;\n }\n }\n return false;\n }\n \n // Click interceptor - capture phase, runs BEFORE onclick handlers\n function interceptClick(event) {\n // ========== DEBUG: phone-21 click logging ==========\n if (isBuggyScreen()) {\n var clickX = event.clientX;\n var clickY = event.clientY;\n var originalTarget = event.target;\n var originalRect = originalTarget.getBoundingClientRect ? originalTarget.getBoundingClientRect() : {};\n debugBuggyScreen('Click', 'Click at (' + clickX + ',' + clickY + ')', {\n originalTarget: {\n tag: originalTarget.tagName,\n id: originalTarget.id || '(none)',\n class: (originalTarget.className && typeof originalTarget.className === 'string') ? originalTarget.className.substring(0,100) : '(none)',\n size: originalRect.width + 'x' + originalRect.height\n }\n });\n }\n // ========== END DEBUG ==========\n\n var result = findDynamicTap(event.target);\n if (!result) return;\n\n // ========== DEBUG: phone-21 - log when dynamic tap is found ==========\n if (isBuggyScreen()) {\n var rect = result.element.getBoundingClientRect ? result.element.getBoundingClientRect() : {};\n var screenWidth = window.innerWidth;\n var screenHeight = window.innerHeight;\n var isFullScreen = (rect.width >= screenWidth * 0.9 && rect.height >= screenHeight * 0.9);\n debugBuggyScreen('Click', 'DYNAMIC TAP WILL BE HANDLED', {\n element: {\n tag: result.element.tagName,\n id: result.element.id || '(none)',\n class: (result.element.className && typeof result.element.className === 'string') ? result.element.className.substring(0,100) : '(none)',\n size: rect.width + 'x' + rect.height\n },\n isFullScreen: isFullScreen,\n WARNING: isFullScreen ? 'FULL SCREEN ELEMENT WITH DYNAMIC TAP - THIS IS THE BUG!' : 'normal element'\n });\n }\n // ========== END DEBUG ==========\n\n try {\n var configStr = decodeHtml(result.config);\n var config = JSON.parse(configStr);\n\n // ========== DEBUG: phone-21 - log parsed config ==========\n if (isBuggyScreen()) {\n debugBuggyScreen('Click', 'Parsed config', {\n hasValues: !!(config && config.values),\n valuesCount: config && config.values ? config.values.length : 0,\n firstCondition: config && config.values && config.values[0] ? {\n conditionType: config.values[0].conditionType,\n rulesCount: config.values[0].rules ? config.values[0].rules.length : 0,\n actionsCount: config.values[0].actions ? config.values[0].actions.length : 0\n } : null\n });\n }\n // ========== END DEBUG ==========\n\n var handled = evalDynamicTap(config);\n if (handled) {\n debugBuggyScreen('Click', 'HANDLED by dynamic tap - event stopped', { handled: true });\n event.stopImmediatePropagation();\n event.preventDefault();\n return false;\n }\n } catch (e) {\n console.log('[RampKit] Dynamic tap error:', e);\n if (isBuggyScreen()) {\n debugBuggyScreen('Click', 'Dynamic tap parse error', { error: e.message });\n }\n }\n }\n\n // Install interceptor on window in capture phase\n window.addEventListener('click', interceptClick, true);\n})();\n";
6
- export declare const injectedButtonAnimations = "\n(function(){\n try {\n if (window.__rkButtonAnimApplied) return true;\n window.__rkButtonAnimApplied = true;\n\n var pressed = null;\n var pressedOriginalTransform = '';\n var pressedOriginalOpacity = '';\n var pressedOriginalTransition = '';\n var releaseTimer = null;\n\n // ========== DEBUG: phone-21 (buggy screen) specific logging (REMOVE AFTER BUG FIX) ==========\n var isBuggyScreenAnim = function() {\n var screenId = window.__rampkitScreenId || '';\n var screenIndex = window.__rampkitScreenIndex;\n return screenId === 'phone-21' || screenIndex === 18;\n };\n\n var debugBuggyScreenAnim = function(source, msg, data) {\n if (isBuggyScreenAnim()) {\n console.log('[BUGGY-SCREEN][' + source + '] ' + msg, JSON.stringify(data || {}));\n // Send to React Native via postMessage for visibility\n try {\n if (window.ReactNativeWebView && window.ReactNativeWebView.postMessage) {\n window.ReactNativeWebView.postMessage(JSON.stringify({\n type: 'rampkit:debug-buggy-screen',\n source: source,\n message: msg,\n data: data\n }));\n }\n } catch(e) {}\n }\n };\n // ========== END DEBUG ==========\n\n // Find interactive element - very permissive, looks for any clickable-looking element\n function findInteractive(el) {\n var current = el;\n var debugTrail = []; // DEBUG: Track search path for Screen 19\n\n for (var i = 0; i < 20 && current && current !== document.body && current !== document.documentElement; i++) {\n if (!current || !current.tagName) { current = current.parentElement; continue; }\n var tag = current.tagName.toLowerCase();\n\n // Skip tiny elements (likely icons inside buttons)\n var rect = current.getBoundingClientRect();\n if (rect.width < 20 || rect.height < 20) { current = current.parentElement; continue; }\n\n // DEBUG: Build trail for Screen 19\n var debugInfo = {\n depth: i,\n tag: tag,\n id: current.id || '(none)',\n class: (current.className && typeof current.className === 'string') ? current.className.substring(0, 100) : '(none)',\n size: rect.width + 'x' + rect.height\n };\n debugTrail.push(debugInfo);\n\n // Match standard interactive elements\n if (tag === 'button' || tag === 'a' || tag === 'input' || tag === 'select') {\n debugBuggyScreenAnim('ButtonAnim', 'MATCHED: standard tag', { reason: 'tag=' + tag, element: debugInfo, trail: debugTrail });\n return current;\n }\n\n // Match elements with any data attribute containing action/navigate/tap/click\n var attrs = current.attributes;\n if (attrs) {\n for (var j = 0; j < attrs.length; j++) {\n var attrName = attrs[j].name.toLowerCase();\n if (attrName.indexOf('click') !== -1 || attrName.indexOf('tap') !== -1 ||\n attrName.indexOf('action') !== -1 || attrName.indexOf('navigate') !== -1 ||\n attrName.indexOf('press') !== -1) {\n debugBuggyScreenAnim('ButtonAnim', 'MATCHED: data-attr', { reason: 'attr=' + attrName, value: attrs[j].value.substring(0,50), element: debugInfo, trail: debugTrail });\n return current;\n }\n }\n }\n\n // Match elements with onclick\n if (current.onclick || current.hasAttribute('onclick')) {\n debugBuggyScreenAnim('ButtonAnim', 'MATCHED: onclick', { element: debugInfo, trail: debugTrail });\n return current;\n }\n\n // Match elements with role=\"button\" or tabindex\n if (current.getAttribute('role') === 'button') {\n debugBuggyScreenAnim('ButtonAnim', 'MATCHED: role=button', { element: debugInfo, trail: debugTrail });\n return current;\n }\n\n // Match any element with an ID containing button/btn/cta\n var id = current.id || '';\n if (id && (id.toLowerCase().indexOf('button') !== -1 || id.toLowerCase().indexOf('btn') !== -1 || id.toLowerCase().indexOf('cta') !== -1)) {\n debugBuggyScreenAnim('ButtonAnim', 'MATCHED: button-like ID', { reason: 'id=' + id, element: debugInfo, trail: debugTrail });\n return current;\n }\n\n // Match elements with button-like classes\n var className = current.className;\n if (className && typeof className === 'string') {\n var cls = className.toLowerCase();\n if (cls.indexOf('btn') !== -1 || cls.indexOf('button') !== -1 || cls.indexOf('cta') !== -1 ||\n cls.indexOf('clickable') !== -1 || cls.indexOf('tappable') !== -1 || cls.indexOf('pressable') !== -1) {\n debugBuggyScreenAnim('ButtonAnim', 'MATCHED: button-like class', { reason: 'class=' + className, element: debugInfo, trail: debugTrail });\n return current;\n }\n }\n\n // Match elements with cursor pointer\n try {\n var computed = window.getComputedStyle(current);\n if (computed && computed.cursor === 'pointer') {\n debugBuggyScreenAnim('ButtonAnim', 'MATCHED: cursor:pointer', { element: debugInfo, trail: debugTrail });\n return current;\n }\n } catch(e) {}\n\n current = current.parentElement;\n }\n debugBuggyScreenAnim('ButtonAnim', 'NO MATCH found', { originalTag: el.tagName, trail: debugTrail });\n return null;\n }\n \n function applyPressedStyle(el) {\n if (!el || !el.style) return;\n // Save original styles\n pressedOriginalTransform = el.style.transform || '';\n pressedOriginalOpacity = el.style.opacity || '';\n pressedOriginalTransition = el.style.transition || '';\n // Apply pressed style with inline styles for maximum specificity\n el.style.transition = 'transform 80ms cubic-bezier(0.25, 0.1, 0.25, 1), opacity 80ms cubic-bezier(0.25, 0.1, 0.25, 1)';\n el.style.transform = 'scale(0.97)';\n el.style.opacity = '0.8';\n }\n \n function applyReleasedStyle(el) {\n if (!el || !el.style) return;\n // Apply spring-back animation\n el.style.transition = 'transform 280ms cubic-bezier(0.34, 1.56, 0.64, 1), opacity 280ms cubic-bezier(0.34, 1.56, 0.64, 1)';\n el.style.transform = pressedOriginalTransform || 'scale(1)';\n el.style.opacity = pressedOriginalOpacity || '1';\n }\n \n function resetStyle(el) {\n if (!el || !el.style) return;\n el.style.transform = pressedOriginalTransform;\n el.style.opacity = pressedOriginalOpacity;\n el.style.transition = pressedOriginalTransition;\n }\n \n function onTouchStart(e) {\n try {\n // ========== DEBUG: phone-21 touch logging ==========\n if (isBuggyScreenAnim()) {\n var touchX = e.touches ? e.touches[0].clientX : e.clientX;\n var touchY = e.touches ? e.touches[0].clientY : e.clientY;\n var originalTarget = e.target;\n var originalRect = originalTarget.getBoundingClientRect ? originalTarget.getBoundingClientRect() : {};\n debugBuggyScreenAnim('TouchStart', 'Touch at (' + touchX + ',' + touchY + ')', {\n originalTarget: {\n tag: originalTarget.tagName,\n id: originalTarget.id || '(none)',\n class: (originalTarget.className && typeof originalTarget.className === 'string') ? originalTarget.className.substring(0,100) : '(none)',\n size: originalRect.width + 'x' + originalRect.height\n }\n });\n }\n // ========== END DEBUG ==========\n\n var target = findInteractive(e.target);\n if (!target) return;\n\n // ========== DEBUG: phone-21 - log what element was found interactive ==========\n if (isBuggyScreenAnim() && target) {\n var targetRect = target.getBoundingClientRect();\n var screenWidth = window.innerWidth;\n var screenHeight = window.innerHeight;\n var isFullScreen = (targetRect.width >= screenWidth * 0.9 && targetRect.height >= screenHeight * 0.9);\n debugBuggyScreenAnim('TouchStart', 'INTERACTIVE ELEMENT FOUND', {\n tag: target.tagName,\n id: target.id || '(none)',\n class: (target.className && typeof target.className === 'string') ? target.className.substring(0,100) : '(none)',\n size: targetRect.width + 'x' + targetRect.height,\n isFullScreen: isFullScreen,\n WARNING: isFullScreen ? 'FULL SCREEN ELEMENT DETECTED - THIS IS THE BUG!' : 'normal element'\n });\n }\n // ========== END DEBUG ==========\n\n if (releaseTimer) { clearTimeout(releaseTimer); releaseTimer = null; }\n if (pressed && pressed !== target) { resetStyle(pressed); }\n applyPressedStyle(target);\n pressed = target;\n } catch(err) {}\n }\n \n function onTouchEnd(e) {\n try {\n if (!pressed) return;\n var t = pressed;\n applyReleasedStyle(t);\n releaseTimer = setTimeout(function() {\n resetStyle(t);\n releaseTimer = null;\n }, 300);\n pressed = null;\n } catch(err) {}\n }\n \n function onTouchCancel(e) {\n try {\n if (!pressed) return;\n resetStyle(pressed);\n pressed = null;\n if (releaseTimer) { clearTimeout(releaseTimer); releaseTimer = null; }\n } catch(err) {}\n }\n \n // Use capture phase for immediate response before any other handlers\n document.addEventListener('touchstart', onTouchStart, { passive: true, capture: true });\n document.addEventListener('touchend', onTouchEnd, { passive: true, capture: true });\n document.addEventListener('touchcancel', onTouchCancel, { passive: true, capture: true });\n // Mouse events for testing\n document.addEventListener('mousedown', onTouchStart, { passive: true, capture: true });\n document.addEventListener('mouseup', onTouchEnd, { passive: true, capture: true });\n \n } catch (err) {}\n true;\n})();\n";
5
+ export declare const injectedDynamicTapHandler = "\n(function() {\n if (window.__rampkitClickInterceptorInstalled) return;\n window.__rampkitClickInterceptorInstalled = true;\n\n // Decode HTML entities\n function decodeHtml(str) {\n if (!str) return str;\n return str.replace(/&quot;/g, '\"').replace(/&#34;/g, '\"').replace(/&#x22;/g, '\"')\n .replace(/&apos;/g, \"'\").replace(/&#39;/g, \"'\").replace(/&#x27;/g, \"'\")\n .replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&amp;/g, '&');\n }\n\n // Find dynamic tap config on element or ancestors\n function findDynamicTap(el) {\n var current = el;\n var depth = 0;\n var attrNames = ['data-tap-dynamic', 'data-tapdynamic', 'tapDynamic', 'data-dynamic-tap'];\n while (current && current !== document.body && current !== document.documentElement && depth < 20) {\n if (current.getAttribute) {\n for (var i = 0; i < attrNames.length; i++) {\n var attr = current.getAttribute(attrNames[i]);\n if (attr && attr.length > 2) {\n return { element: current, config: attr };\n }\n }\n if (current.dataset && current.dataset.tapDynamic) {\n return { element: current, config: current.dataset.tapDynamic };\n }\n }\n current = current.parentElement;\n depth++;\n }\n return null;\n }\n \n // Get variables for condition evaluation - check ALL possible sources\n function getVars() {\n var vars = {};\n if (window.__rampkitVariables) {\n Object.keys(window.__rampkitVariables).forEach(function(k) {\n vars[k] = window.__rampkitVariables[k];\n });\n }\n if (window.__rampkitVars) {\n Object.keys(window.__rampkitVars).forEach(function(k) {\n vars[k] = window.__rampkitVars[k];\n });\n }\n if (window.RK_VARS) {\n Object.keys(window.RK_VARS).forEach(function(k) {\n vars[k] = window.RK_VARS[k];\n });\n }\n return vars;\n }\n \n // Evaluate a single rule\n function evalRule(rule, vars) {\n if (!rule || !rule.key) return false;\n var left = vars[rule.key];\n var right = rule.value;\n var op = rule.op || '=';\n if (left === undefined || left === null) left = '';\n if (right === undefined || right === null) right = '';\n var leftStr = String(left);\n var rightStr = String(right);\n var result = false;\n switch (op) {\n case '=': case '==': result = leftStr === rightStr; break;\n case '!=': case '<>': result = leftStr !== rightStr; break;\n case '>': result = parseFloat(left) > parseFloat(right); break;\n case '<': result = parseFloat(left) < parseFloat(right); break;\n case '>=': result = parseFloat(left) >= parseFloat(right); break;\n case '<=': result = parseFloat(left) <= parseFloat(right); break;\n default: result = false;\n }\n return result;\n }\n \n // Evaluate all rules (AND logic)\n function evalRules(rules, vars) {\n if (!rules || !rules.length) return true;\n for (var i = 0; i < rules.length; i++) {\n if (!evalRule(rules[i], vars)) return false;\n }\n return true;\n }\n \n // Execute an action\n function execAction(action) {\n if (!action || !action.type) return;\n var msg = null;\n var actionType = action.type.toLowerCase();\n \n switch (actionType) {\n case 'navigate':\n msg = { type: 'rampkit:navigate', targetScreenId: action.targetScreenId || '__continue__', animation: action.animation || 'fade' };\n break;\n case 'continue':\n msg = { type: 'rampkit:navigate', targetScreenId: '__continue__', animation: action.animation || 'fade' };\n break;\n case 'goback':\n msg = { type: 'rampkit:goBack', animation: action.animation || 'fade' };\n break;\n case 'close':\n msg = { type: 'rampkit:close' };\n break;\n case 'haptic':\n msg = { type: 'rampkit:haptic', hapticType: action.hapticType || 'impact', impactStyle: action.impactStyle || 'Medium', notificationType: action.notificationType };\n break;\n case 'showpaywall':\n msg = { type: 'rampkit:show-paywall', payload: action.payload || { paywallId: action.paywallId } };\n break;\n case 'requestreview':\n msg = { type: 'rampkit:request-review' };\n break;\n case 'requestnotificationpermission':\n msg = { type: 'rampkit:request-notification-permission' };\n break;\n case 'onboardingfinished':\n msg = { type: 'rampkit:onboarding-finished', payload: action.payload };\n break;\n case 'setvariable':\n case 'setstate':\n case 'updatevariable':\n case 'set':\n case 'assign':\n var varKey = action.key || action.variableName || action.name || action.variable;\n var varValue = action.variableValue !== undefined ? action.variableValue :\n action.value !== undefined ? action.value :\n action.newValue !== undefined ? action.newValue : undefined;\n if (varKey && varValue !== undefined) {\n if (window.__rampkitVariables) window.__rampkitVariables[varKey] = varValue;\n if (window.__rampkitVars) window.__rampkitVars[varKey] = varValue;\n var updateVars = {};\n updateVars[varKey] = varValue;\n msg = { type: 'rampkit:variables', vars: updateVars };\n }\n break;\n }\n if (msg) {\n try {\n if (window.ReactNativeWebView && window.ReactNativeWebView.postMessage) {\n window.ReactNativeWebView.postMessage(JSON.stringify(msg));\n }\n } catch(e) {}\n }\n }\n \n // Evaluate dynamic tap config\n function evalDynamicTap(config) {\n if (!config || !config.values) return false;\n var vars = getVars();\n var conditions = config.values;\n for (var i = 0; i < conditions.length; i++) {\n var cond = conditions[i];\n var condType = cond.conditionType || 'if';\n var rules = cond.rules || [];\n var actions = cond.actions || [];\n if (condType === 'else' || evalRules(rules, vars)) {\n for (var j = 0; j < actions.length; j++) {\n execAction(actions[j]);\n }\n return true;\n }\n }\n return false;\n }\n \n // Click interceptor - capture phase, runs BEFORE onclick handlers\n function interceptClick(event) {\n var result = findDynamicTap(event.target);\n if (!result) return;\n\n try {\n var configStr = decodeHtml(result.config);\n var config = JSON.parse(configStr);\n var handled = evalDynamicTap(config);\n if (handled) {\n event.stopImmediatePropagation();\n event.preventDefault();\n return false;\n }\n } catch (e) {\n console.log('[RampKit] Dynamic tap error:', e);\n }\n }\n\n // Install interceptor on window in capture phase\n window.addEventListener('click', interceptClick, true);\n})();\n";
6
+ export declare const injectedButtonAnimations = "\n(function(){\n try {\n if (window.__rkButtonAnimApplied) return true;\n window.__rkButtonAnimApplied = true;\n\n var pressed = null;\n var pressedOriginalTransform = '';\n var pressedOriginalOpacity = '';\n var pressedOriginalTransition = '';\n var releaseTimer = null;\n\n // Find interactive element - looks for clickable-looking elements\n function findInteractive(el) {\n var current = el;\n for (var i = 0; i < 20 && current && current !== document.body && current !== document.documentElement; i++) {\n if (!current || !current.tagName) { current = current.parentElement; continue; }\n var tag = current.tagName.toLowerCase();\n\n // Skip tiny elements (likely icons inside buttons)\n var rect = current.getBoundingClientRect();\n if (rect.width < 20 || rect.height < 20) { current = current.parentElement; continue; }\n\n // Match standard interactive elements\n if (tag === 'button' || tag === 'a' || tag === 'input' || tag === 'select') return current;\n\n // Match elements with tap/click-related data attributes\n // Exclude lifecycle attributes like data-on-open-actions, data-on-close-actions\n var attrs = current.attributes;\n if (attrs) {\n for (var j = 0; j < attrs.length; j++) {\n var attrName = attrs[j].name.toLowerCase();\n // Skip lifecycle attributes (on-open, on-close, on-load, on-appear, etc.)\n // These are for screen lifecycle events, not user tap interactions\n var isLifecycleAttr = (attrName.indexOf('on-open') !== -1 || attrName.indexOf('on-close') !== -1 ||\n attrName.indexOf('on-load') !== -1 || attrName.indexOf('on-appear') !== -1 ||\n attrName.indexOf('onopen') !== -1 || attrName.indexOf('onclose') !== -1 ||\n attrName.indexOf('onload') !== -1 || attrName.indexOf('onappear') !== -1);\n if (isLifecycleAttr) {\n continue;\n }\n // Match tap/click interaction attributes\n if (attrName.indexOf('click') !== -1 || attrName.indexOf('tap') !== -1 ||\n attrName.indexOf('action') !== -1 || attrName.indexOf('navigate') !== -1 ||\n attrName.indexOf('press') !== -1) {\n return current;\n }\n }\n }\n\n // Match elements with onclick\n if (current.onclick || current.hasAttribute('onclick')) return current;\n\n // Match elements with role=\"button\"\n if (current.getAttribute('role') === 'button') return current;\n\n // Match any element with an ID containing button/btn/cta\n var id = current.id || '';\n if (id && (id.toLowerCase().indexOf('button') !== -1 || id.toLowerCase().indexOf('btn') !== -1 || id.toLowerCase().indexOf('cta') !== -1)) return current;\n\n // Match elements with button-like classes\n var className = current.className;\n if (className && typeof className === 'string') {\n var cls = className.toLowerCase();\n if (cls.indexOf('btn') !== -1 || cls.indexOf('button') !== -1 || cls.indexOf('cta') !== -1 ||\n cls.indexOf('clickable') !== -1 || cls.indexOf('tappable') !== -1 || cls.indexOf('pressable') !== -1) {\n return current;\n }\n }\n\n // Match elements with cursor pointer\n try {\n var computed = window.getComputedStyle(current);\n if (computed && computed.cursor === 'pointer') return current;\n } catch(e) {}\n\n current = current.parentElement;\n }\n return null;\n }\n\n function applyPressedStyle(el) {\n if (!el || !el.style) return;\n // Save original styles\n pressedOriginalTransform = el.style.transform || '';\n pressedOriginalOpacity = el.style.opacity || '';\n pressedOriginalTransition = el.style.transition || '';\n // Apply pressed style with inline styles for maximum specificity\n el.style.transition = 'transform 80ms cubic-bezier(0.25, 0.1, 0.25, 1), opacity 80ms cubic-bezier(0.25, 0.1, 0.25, 1)';\n el.style.transform = 'scale(0.97)';\n el.style.opacity = '0.8';\n }\n\n function applyReleasedStyle(el) {\n if (!el || !el.style) return;\n // Apply spring-back animation\n el.style.transition = 'transform 280ms cubic-bezier(0.34, 1.56, 0.64, 1), opacity 280ms cubic-bezier(0.34, 1.56, 0.64, 1)';\n el.style.transform = pressedOriginalTransform || 'scale(1)';\n el.style.opacity = pressedOriginalOpacity || '1';\n }\n\n function resetStyle(el) {\n if (!el || !el.style) return;\n el.style.transform = pressedOriginalTransform;\n el.style.opacity = pressedOriginalOpacity;\n el.style.transition = pressedOriginalTransition;\n }\n\n function onTouchStart(e) {\n try {\n var target = findInteractive(e.target);\n if (!target) return;\n if (releaseTimer) { clearTimeout(releaseTimer); releaseTimer = null; }\n if (pressed && pressed !== target) { resetStyle(pressed); }\n applyPressedStyle(target);\n pressed = target;\n } catch(err) {}\n }\n \n function onTouchEnd(e) {\n try {\n if (!pressed) return;\n var t = pressed;\n applyReleasedStyle(t);\n releaseTimer = setTimeout(function() {\n resetStyle(t);\n releaseTimer = null;\n }, 300);\n pressed = null;\n } catch(err) {}\n }\n \n function onTouchCancel(e) {\n try {\n if (!pressed) return;\n resetStyle(pressed);\n pressed = null;\n if (releaseTimer) { clearTimeout(releaseTimer); releaseTimer = null; }\n } catch(err) {}\n }\n \n // Use capture phase for immediate response before any other handlers\n document.addEventListener('touchstart', onTouchStart, { passive: true, capture: true });\n document.addEventListener('touchend', onTouchEnd, { passive: true, capture: true });\n document.addEventListener('touchcancel', onTouchCancel, { passive: true, capture: true });\n // Mouse events for testing\n document.addEventListener('mousedown', onTouchStart, { passive: true, capture: true });\n document.addEventListener('mouseup', onTouchEnd, { passive: true, capture: true });\n \n } catch (err) {}\n true;\n})();\n";
7
7
  export type ScreenPayload = {
8
8
  id: string;
9
9
  html: string;
@@ -182,32 +182,6 @@ exports.injectedDynamicTapHandler = `
182
182
  if (window.__rampkitClickInterceptorInstalled) return;
183
183
  window.__rampkitClickInterceptorInstalled = true;
184
184
 
185
- // ========== DEBUG: phone-21 (buggy screen) specific logging (REMOVE AFTER BUG FIX) ==========
186
- var isBuggyScreen = function() {
187
- var screenId = window.__rampkitScreenId || '';
188
- var screenIndex = window.__rampkitScreenIndex;
189
- return screenId === 'phone-21' || screenIndex === 18;
190
- };
191
-
192
- var debugBuggyScreen = function(source, msg, data) {
193
- if (isBuggyScreen()) {
194
- var logData = { source: source, msg: msg, data: data };
195
- console.log('[BUGGY-SCREEN][' + source + '] ' + msg, JSON.stringify(data || {}));
196
- // Send to React Native via postMessage for visibility in RN console
197
- try {
198
- if (window.ReactNativeWebView && window.ReactNativeWebView.postMessage) {
199
- window.ReactNativeWebView.postMessage(JSON.stringify({
200
- type: 'rampkit:debug-buggy-screen',
201
- source: source,
202
- message: msg,
203
- data: data
204
- }));
205
- }
206
- } catch(e) {}
207
- }
208
- };
209
- // ========== END DEBUG ==========
210
-
211
185
  // Decode HTML entities
212
186
  function decodeHtml(str) {
213
187
  if (!str) return str;
@@ -221,49 +195,21 @@ exports.injectedDynamicTapHandler = `
221
195
  var current = el;
222
196
  var depth = 0;
223
197
  var attrNames = ['data-tap-dynamic', 'data-tapdynamic', 'tapDynamic', 'data-dynamic-tap'];
224
- var searchTrail = []; // DEBUG: Track search path
225
-
226
198
  while (current && current !== document.body && current !== document.documentElement && depth < 20) {
227
- // DEBUG: Build search trail for Screen 19
228
- var trailInfo = {
229
- depth: depth,
230
- tag: current.tagName,
231
- id: current.id || '(none)',
232
- class: (current.className && typeof current.className === 'string') ? current.className.substring(0,50) : '(none)'
233
- };
234
- searchTrail.push(trailInfo);
235
-
236
199
  if (current.getAttribute) {
237
200
  for (var i = 0; i < attrNames.length; i++) {
238
201
  var attr = current.getAttribute(attrNames[i]);
239
202
  if (attr && attr.length > 2) {
240
- var rect = current.getBoundingClientRect ? current.getBoundingClientRect() : {};
241
- debugBuggyScreen('DynamicTap', 'FOUND data-tap-dynamic', {
242
- attrName: attrNames[i],
243
- depth: depth,
244
- element: trailInfo,
245
- size: rect.width + 'x' + rect.height,
246
- configPreview: attr.substring(0, 100) + '...',
247
- trail: searchTrail
248
- });
249
203
  return { element: current, config: attr };
250
204
  }
251
205
  }
252
206
  if (current.dataset && current.dataset.tapDynamic) {
253
- var rect2 = current.getBoundingClientRect ? current.getBoundingClientRect() : {};
254
- debugBuggyScreen('DynamicTap', 'FOUND dataset.tapDynamic', {
255
- depth: depth,
256
- element: trailInfo,
257
- size: rect2.width + 'x' + rect2.height,
258
- trail: searchTrail
259
- });
260
207
  return { element: current, config: current.dataset.tapDynamic };
261
208
  }
262
209
  }
263
210
  current = current.parentElement;
264
211
  depth++;
265
212
  }
266
- debugBuggyScreen('DynamicTap', 'NO data-tap-dynamic found', { trail: searchTrail });
267
213
  return null;
268
214
  }
269
215
 
@@ -403,75 +349,20 @@ exports.injectedDynamicTapHandler = `
403
349
 
404
350
  // Click interceptor - capture phase, runs BEFORE onclick handlers
405
351
  function interceptClick(event) {
406
- // ========== DEBUG: phone-21 click logging ==========
407
- if (isBuggyScreen()) {
408
- var clickX = event.clientX;
409
- var clickY = event.clientY;
410
- var originalTarget = event.target;
411
- var originalRect = originalTarget.getBoundingClientRect ? originalTarget.getBoundingClientRect() : {};
412
- debugBuggyScreen('Click', 'Click at (' + clickX + ',' + clickY + ')', {
413
- originalTarget: {
414
- tag: originalTarget.tagName,
415
- id: originalTarget.id || '(none)',
416
- class: (originalTarget.className && typeof originalTarget.className === 'string') ? originalTarget.className.substring(0,100) : '(none)',
417
- size: originalRect.width + 'x' + originalRect.height
418
- }
419
- });
420
- }
421
- // ========== END DEBUG ==========
422
-
423
352
  var result = findDynamicTap(event.target);
424
353
  if (!result) return;
425
354
 
426
- // ========== DEBUG: phone-21 - log when dynamic tap is found ==========
427
- if (isBuggyScreen()) {
428
- var rect = result.element.getBoundingClientRect ? result.element.getBoundingClientRect() : {};
429
- var screenWidth = window.innerWidth;
430
- var screenHeight = window.innerHeight;
431
- var isFullScreen = (rect.width >= screenWidth * 0.9 && rect.height >= screenHeight * 0.9);
432
- debugBuggyScreen('Click', 'DYNAMIC TAP WILL BE HANDLED', {
433
- element: {
434
- tag: result.element.tagName,
435
- id: result.element.id || '(none)',
436
- class: (result.element.className && typeof result.element.className === 'string') ? result.element.className.substring(0,100) : '(none)',
437
- size: rect.width + 'x' + rect.height
438
- },
439
- isFullScreen: isFullScreen,
440
- WARNING: isFullScreen ? 'FULL SCREEN ELEMENT WITH DYNAMIC TAP - THIS IS THE BUG!' : 'normal element'
441
- });
442
- }
443
- // ========== END DEBUG ==========
444
-
445
355
  try {
446
356
  var configStr = decodeHtml(result.config);
447
357
  var config = JSON.parse(configStr);
448
-
449
- // ========== DEBUG: phone-21 - log parsed config ==========
450
- if (isBuggyScreen()) {
451
- debugBuggyScreen('Click', 'Parsed config', {
452
- hasValues: !!(config && config.values),
453
- valuesCount: config && config.values ? config.values.length : 0,
454
- firstCondition: config && config.values && config.values[0] ? {
455
- conditionType: config.values[0].conditionType,
456
- rulesCount: config.values[0].rules ? config.values[0].rules.length : 0,
457
- actionsCount: config.values[0].actions ? config.values[0].actions.length : 0
458
- } : null
459
- });
460
- }
461
- // ========== END DEBUG ==========
462
-
463
358
  var handled = evalDynamicTap(config);
464
359
  if (handled) {
465
- debugBuggyScreen('Click', 'HANDLED by dynamic tap - event stopped', { handled: true });
466
360
  event.stopImmediatePropagation();
467
361
  event.preventDefault();
468
362
  return false;
469
363
  }
470
364
  } catch (e) {
471
365
  console.log('[RampKit] Dynamic tap error:', e);
472
- if (isBuggyScreen()) {
473
- debugBuggyScreen('Click', 'Dynamic tap parse error', { error: e.message });
474
- }
475
366
  }
476
367
  }
477
368
 
@@ -494,36 +385,9 @@ exports.injectedButtonAnimations = `
494
385
  var pressedOriginalTransition = '';
495
386
  var releaseTimer = null;
496
387
 
497
- // ========== DEBUG: phone-21 (buggy screen) specific logging (REMOVE AFTER BUG FIX) ==========
498
- var isBuggyScreenAnim = function() {
499
- var screenId = window.__rampkitScreenId || '';
500
- var screenIndex = window.__rampkitScreenIndex;
501
- return screenId === 'phone-21' || screenIndex === 18;
502
- };
503
-
504
- var debugBuggyScreenAnim = function(source, msg, data) {
505
- if (isBuggyScreenAnim()) {
506
- console.log('[BUGGY-SCREEN][' + source + '] ' + msg, JSON.stringify(data || {}));
507
- // Send to React Native via postMessage for visibility
508
- try {
509
- if (window.ReactNativeWebView && window.ReactNativeWebView.postMessage) {
510
- window.ReactNativeWebView.postMessage(JSON.stringify({
511
- type: 'rampkit:debug-buggy-screen',
512
- source: source,
513
- message: msg,
514
- data: data
515
- }));
516
- }
517
- } catch(e) {}
518
- }
519
- };
520
- // ========== END DEBUG ==========
521
-
522
- // Find interactive element - very permissive, looks for any clickable-looking element
388
+ // Find interactive element - looks for clickable-looking elements
523
389
  function findInteractive(el) {
524
390
  var current = el;
525
- var debugTrail = []; // DEBUG: Track search path for Screen 19
526
-
527
391
  for (var i = 0; i < 20 && current && current !== document.body && current !== document.documentElement; i++) {
528
392
  if (!current || !current.tagName) { current = current.parentElement; continue; }
529
393
  var tag = current.tagName.toLowerCase();
@@ -532,54 +396,42 @@ exports.injectedButtonAnimations = `
532
396
  var rect = current.getBoundingClientRect();
533
397
  if (rect.width < 20 || rect.height < 20) { current = current.parentElement; continue; }
534
398
 
535
- // DEBUG: Build trail for Screen 19
536
- var debugInfo = {
537
- depth: i,
538
- tag: tag,
539
- id: current.id || '(none)',
540
- class: (current.className && typeof current.className === 'string') ? current.className.substring(0, 100) : '(none)',
541
- size: rect.width + 'x' + rect.height
542
- };
543
- debugTrail.push(debugInfo);
544
-
545
399
  // Match standard interactive elements
546
- if (tag === 'button' || tag === 'a' || tag === 'input' || tag === 'select') {
547
- debugBuggyScreenAnim('ButtonAnim', 'MATCHED: standard tag', { reason: 'tag=' + tag, element: debugInfo, trail: debugTrail });
548
- return current;
549
- }
400
+ if (tag === 'button' || tag === 'a' || tag === 'input' || tag === 'select') return current;
550
401
 
551
- // Match elements with any data attribute containing action/navigate/tap/click
402
+ // Match elements with tap/click-related data attributes
403
+ // Exclude lifecycle attributes like data-on-open-actions, data-on-close-actions
552
404
  var attrs = current.attributes;
553
405
  if (attrs) {
554
406
  for (var j = 0; j < attrs.length; j++) {
555
407
  var attrName = attrs[j].name.toLowerCase();
408
+ // Skip lifecycle attributes (on-open, on-close, on-load, on-appear, etc.)
409
+ // These are for screen lifecycle events, not user tap interactions
410
+ var isLifecycleAttr = (attrName.indexOf('on-open') !== -1 || attrName.indexOf('on-close') !== -1 ||
411
+ attrName.indexOf('on-load') !== -1 || attrName.indexOf('on-appear') !== -1 ||
412
+ attrName.indexOf('onopen') !== -1 || attrName.indexOf('onclose') !== -1 ||
413
+ attrName.indexOf('onload') !== -1 || attrName.indexOf('onappear') !== -1);
414
+ if (isLifecycleAttr) {
415
+ continue;
416
+ }
417
+ // Match tap/click interaction attributes
556
418
  if (attrName.indexOf('click') !== -1 || attrName.indexOf('tap') !== -1 ||
557
419
  attrName.indexOf('action') !== -1 || attrName.indexOf('navigate') !== -1 ||
558
420
  attrName.indexOf('press') !== -1) {
559
- debugBuggyScreenAnim('ButtonAnim', 'MATCHED: data-attr', { reason: 'attr=' + attrName, value: attrs[j].value.substring(0,50), element: debugInfo, trail: debugTrail });
560
421
  return current;
561
422
  }
562
423
  }
563
424
  }
564
425
 
565
426
  // Match elements with onclick
566
- if (current.onclick || current.hasAttribute('onclick')) {
567
- debugBuggyScreenAnim('ButtonAnim', 'MATCHED: onclick', { element: debugInfo, trail: debugTrail });
568
- return current;
569
- }
427
+ if (current.onclick || current.hasAttribute('onclick')) return current;
570
428
 
571
- // Match elements with role="button" or tabindex
572
- if (current.getAttribute('role') === 'button') {
573
- debugBuggyScreenAnim('ButtonAnim', 'MATCHED: role=button', { element: debugInfo, trail: debugTrail });
574
- return current;
575
- }
429
+ // Match elements with role="button"
430
+ if (current.getAttribute('role') === 'button') return current;
576
431
 
577
432
  // Match any element with an ID containing button/btn/cta
578
433
  var id = current.id || '';
579
- if (id && (id.toLowerCase().indexOf('button') !== -1 || id.toLowerCase().indexOf('btn') !== -1 || id.toLowerCase().indexOf('cta') !== -1)) {
580
- debugBuggyScreenAnim('ButtonAnim', 'MATCHED: button-like ID', { reason: 'id=' + id, element: debugInfo, trail: debugTrail });
581
- return current;
582
- }
434
+ if (id && (id.toLowerCase().indexOf('button') !== -1 || id.toLowerCase().indexOf('btn') !== -1 || id.toLowerCase().indexOf('cta') !== -1)) return current;
583
435
 
584
436
  // Match elements with button-like classes
585
437
  var className = current.className;
@@ -587,7 +439,6 @@ exports.injectedButtonAnimations = `
587
439
  var cls = className.toLowerCase();
588
440
  if (cls.indexOf('btn') !== -1 || cls.indexOf('button') !== -1 || cls.indexOf('cta') !== -1 ||
589
441
  cls.indexOf('clickable') !== -1 || cls.indexOf('tappable') !== -1 || cls.indexOf('pressable') !== -1) {
590
- debugBuggyScreenAnim('ButtonAnim', 'MATCHED: button-like class', { reason: 'class=' + className, element: debugInfo, trail: debugTrail });
591
442
  return current;
592
443
  }
593
444
  }
@@ -595,18 +446,14 @@ exports.injectedButtonAnimations = `
595
446
  // Match elements with cursor pointer
596
447
  try {
597
448
  var computed = window.getComputedStyle(current);
598
- if (computed && computed.cursor === 'pointer') {
599
- debugBuggyScreenAnim('ButtonAnim', 'MATCHED: cursor:pointer', { element: debugInfo, trail: debugTrail });
600
- return current;
601
- }
449
+ if (computed && computed.cursor === 'pointer') return current;
602
450
  } catch(e) {}
603
451
 
604
452
  current = current.parentElement;
605
453
  }
606
- debugBuggyScreenAnim('ButtonAnim', 'NO MATCH found', { originalTag: el.tagName, trail: debugTrail });
607
454
  return null;
608
455
  }
609
-
456
+
610
457
  function applyPressedStyle(el) {
611
458
  if (!el || !el.style) return;
612
459
  // Save original styles
@@ -618,7 +465,7 @@ exports.injectedButtonAnimations = `
618
465
  el.style.transform = 'scale(0.97)';
619
466
  el.style.opacity = '0.8';
620
467
  }
621
-
468
+
622
469
  function applyReleasedStyle(el) {
623
470
  if (!el || !el.style) return;
624
471
  // Apply spring-back animation
@@ -626,53 +473,18 @@ exports.injectedButtonAnimations = `
626
473
  el.style.transform = pressedOriginalTransform || 'scale(1)';
627
474
  el.style.opacity = pressedOriginalOpacity || '1';
628
475
  }
629
-
476
+
630
477
  function resetStyle(el) {
631
478
  if (!el || !el.style) return;
632
479
  el.style.transform = pressedOriginalTransform;
633
480
  el.style.opacity = pressedOriginalOpacity;
634
481
  el.style.transition = pressedOriginalTransition;
635
482
  }
636
-
483
+
637
484
  function onTouchStart(e) {
638
485
  try {
639
- // ========== DEBUG: phone-21 touch logging ==========
640
- if (isBuggyScreenAnim()) {
641
- var touchX = e.touches ? e.touches[0].clientX : e.clientX;
642
- var touchY = e.touches ? e.touches[0].clientY : e.clientY;
643
- var originalTarget = e.target;
644
- var originalRect = originalTarget.getBoundingClientRect ? originalTarget.getBoundingClientRect() : {};
645
- debugBuggyScreenAnim('TouchStart', 'Touch at (' + touchX + ',' + touchY + ')', {
646
- originalTarget: {
647
- tag: originalTarget.tagName,
648
- id: originalTarget.id || '(none)',
649
- class: (originalTarget.className && typeof originalTarget.className === 'string') ? originalTarget.className.substring(0,100) : '(none)',
650
- size: originalRect.width + 'x' + originalRect.height
651
- }
652
- });
653
- }
654
- // ========== END DEBUG ==========
655
-
656
486
  var target = findInteractive(e.target);
657
487
  if (!target) return;
658
-
659
- // ========== DEBUG: phone-21 - log what element was found interactive ==========
660
- if (isBuggyScreenAnim() && target) {
661
- var targetRect = target.getBoundingClientRect();
662
- var screenWidth = window.innerWidth;
663
- var screenHeight = window.innerHeight;
664
- var isFullScreen = (targetRect.width >= screenWidth * 0.9 && targetRect.height >= screenHeight * 0.9);
665
- debugBuggyScreenAnim('TouchStart', 'INTERACTIVE ELEMENT FOUND', {
666
- tag: target.tagName,
667
- id: target.id || '(none)',
668
- class: (target.className && typeof target.className === 'string') ? target.className.substring(0,100) : '(none)',
669
- size: targetRect.width + 'x' + targetRect.height,
670
- isFullScreen: isFullScreen,
671
- WARNING: isFullScreen ? 'FULL SCREEN ELEMENT DETECTED - THIS IS THE BUG!' : 'normal element'
672
- });
673
- }
674
- // ========== END DEBUG ==========
675
-
676
488
  if (releaseTimer) { clearTimeout(releaseTimer); releaseTimer = null; }
677
489
  if (pressed && pressed !== target) { resetStyle(pressed); }
678
490
  applyPressedStyle(target);
@@ -2017,29 +1829,9 @@ function Overlay(props) {
2017
1829
  }
2018
1830
  }
2019
1831
  }, onMessage: (ev) => {
2020
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
1832
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
2021
1833
  const raw = ev.nativeEvent.data;
2022
1834
  console.log("raw", raw);
2023
- // ========== DEBUG: phone-21 onMessage logging (REMOVE AFTER BUG FIX) ==========
2024
- const screenId = ((_a = props.screens[i]) === null || _a === void 0 ? void 0 : _a.id) || "";
2025
- const isBuggyScreenOnMsg = screenId === 'phone-21' || i === 18;
2026
- if (isBuggyScreenOnMsg) {
2027
- console.log(`[BUGGY-SCREEN][onMessage] Received message from screen ${i} (ID: ${screenId})`, JSON.stringify({
2028
- rawPreview: raw.substring(0, 200),
2029
- isActiveScreen: i === activeScreenIndexRef.current,
2030
- activeScreenIndex: activeScreenIndexRef.current
2031
- }));
2032
- }
2033
- // Also handle debug messages from WebView
2034
- try {
2035
- const debugData = JSON.parse(raw);
2036
- if ((debugData === null || debugData === void 0 ? void 0 : debugData.type) === 'rampkit:debug-buggy-screen') {
2037
- console.log(`[BUGGY-SCREEN][${debugData.source}] ${debugData.message}`, JSON.stringify(debugData.data || {}));
2038
- return; // Don't process further, it's just a debug message
2039
- }
2040
- }
2041
- catch (_) { }
2042
- // ========== END DEBUG ==========
2043
1835
  // Accept either raw strings or JSON payloads from your editor
2044
1836
  try {
2045
1837
  // JSON path
@@ -2165,7 +1957,7 @@ function Overlay(props) {
2165
1957
  if ((data === null || data === void 0 ? void 0 : data.type) === "rampkit:onboarding-finished") {
2166
1958
  setOnboardingCompleted(true);
2167
1959
  try {
2168
- (_b = props.onOnboardingFinished) === null || _b === void 0 ? void 0 : _b.call(props, data === null || data === void 0 ? void 0 : data.payload);
1960
+ (_a = props.onOnboardingFinished) === null || _a === void 0 ? void 0 : _a.call(props, data === null || data === void 0 ? void 0 : data.payload);
2169
1961
  }
2170
1962
  catch (_) { }
2171
1963
  handleRequestClose({ completed: true });
@@ -2174,7 +1966,7 @@ function Overlay(props) {
2174
1966
  // 6) Request to show paywall
2175
1967
  if ((data === null || data === void 0 ? void 0 : data.type) === "rampkit:show-paywall") {
2176
1968
  try {
2177
- (_c = props.onShowPaywall) === null || _c === void 0 ? void 0 : _c.call(props, data === null || data === void 0 ? void 0 : data.payload);
1969
+ (_b = props.onShowPaywall) === null || _b === void 0 ? void 0 : _b.call(props, data === null || data === void 0 ? void 0 : data.payload);
2178
1970
  }
2179
1971
  catch (_) { }
2180
1972
  return;
@@ -2185,9 +1977,9 @@ function Overlay(props) {
2185
1977
  if (questionId) {
2186
1978
  const response = {
2187
1979
  questionId,
2188
- answer: (_d = data === null || data === void 0 ? void 0 : data.answer) !== null && _d !== void 0 ? _d : "",
1980
+ answer: (_c = data === null || data === void 0 ? void 0 : data.answer) !== null && _c !== void 0 ? _c : "",
2189
1981
  questionText: data === null || data === void 0 ? void 0 : data.questionText,
2190
- screenName: (_e = props.screens[i]) === null || _e === void 0 ? void 0 : _e.id,
1982
+ screenName: (_d = props.screens[i]) === null || _d === void 0 ? void 0 : _d.id,
2191
1983
  answeredAt: new Date().toISOString(),
2192
1984
  };
2193
1985
  OnboardingResponseStorage_1.OnboardingResponseStorage.saveResponse(response);
@@ -2196,71 +1988,62 @@ function Overlay(props) {
2196
1988
  }
2197
1989
  if ((data === null || data === void 0 ? void 0 : data.type) === "rampkit:continue" ||
2198
1990
  (data === null || data === void 0 ? void 0 : data.type) === "continue") {
2199
- // Only process continue from the active screen
1991
+ const executeContinue = () => handleAdvance(i, (data === null || data === void 0 ? void 0 : data.animation) || "fade");
1992
+ // Queue if screen is not active (e.g., on-open actions during preload)
2200
1993
  if (!isScreenActive(i)) {
2201
1994
  if (__DEV__)
2202
- console.log(`[RampKit] Ignoring continue from inactive screen ${i}`);
1995
+ console.log(`[Rampkit] Queuing continue from inactive screen ${i}`);
1996
+ queueAction(i, executeContinue);
2203
1997
  return;
2204
1998
  }
2205
- // ========== DEBUG: phone-21 continue logging ==========
2206
- if (isBuggyScreenOnMsg) {
2207
- console.log(`[BUGGY-SCREEN][onMessage] CONTINUE action triggered from screen ${i}`, JSON.stringify({
2208
- animation: (data === null || data === void 0 ? void 0 : data.animation) || "fade",
2209
- screenId: screenId
2210
- }));
2211
- }
2212
- // ========== END DEBUG ==========
2213
- handleAdvance(i, (data === null || data === void 0 ? void 0 : data.animation) || "fade");
1999
+ executeContinue();
2214
2000
  return;
2215
2001
  }
2216
2002
  if ((data === null || data === void 0 ? void 0 : data.type) === "rampkit:navigate") {
2217
- // Only process navigate from the active screen
2003
+ const target = data === null || data === void 0 ? void 0 : data.targetScreenId;
2004
+ const executeNavigate = () => {
2005
+ if (target === "__goBack__") {
2006
+ handleGoBack(i, (data === null || data === void 0 ? void 0 : data.animation) || "fade");
2007
+ return;
2008
+ }
2009
+ if (!target || target === "__continue__") {
2010
+ handleAdvance(i, (data === null || data === void 0 ? void 0 : data.animation) || "fade");
2011
+ return;
2012
+ }
2013
+ const targetIndex = props.screens.findIndex((s) => s.id === target);
2014
+ if (targetIndex >= 0) {
2015
+ navigateToIndex(targetIndex, (data === null || data === void 0 ? void 0 : data.animation) || "fade");
2016
+ }
2017
+ else {
2018
+ handleAdvance(i);
2019
+ }
2020
+ };
2021
+ // Queue if screen is not active (e.g., on-open actions during preload)
2218
2022
  if (!isScreenActive(i)) {
2219
2023
  if (__DEV__)
2220
- console.log(`[RampKit] Ignoring navigate from inactive screen ${i}`);
2024
+ console.log(`[Rampkit] Queuing navigate from inactive screen ${i}`);
2025
+ queueAction(i, executeNavigate);
2221
2026
  return;
2222
2027
  }
2223
- const target = data === null || data === void 0 ? void 0 : data.targetScreenId;
2224
- // ========== DEBUG: phone-21 navigate logging ==========
2225
- if (isBuggyScreenOnMsg) {
2226
- console.log(`[BUGGY-SCREEN][onMessage] NAVIGATE action triggered from screen ${i}`, JSON.stringify({
2227
- target: target,
2228
- animation: (data === null || data === void 0 ? void 0 : data.animation) || "fade",
2229
- screenId: screenId
2230
- }));
2231
- }
2232
- // ========== END DEBUG ==========
2233
- if (target === "__goBack__") {
2234
- handleGoBack(i, (data === null || data === void 0 ? void 0 : data.animation) || "fade");
2235
- return;
2236
- }
2237
- if (!target || target === "__continue__") {
2238
- handleAdvance(i, (data === null || data === void 0 ? void 0 : data.animation) || "fade");
2239
- return;
2240
- }
2241
- const targetIndex = props.screens.findIndex((s) => s.id === target);
2242
- if (targetIndex >= 0) {
2243
- navigateToIndex(targetIndex, (data === null || data === void 0 ? void 0 : data.animation) || "fade");
2244
- }
2245
- else {
2246
- handleAdvance(i);
2247
- }
2028
+ executeNavigate();
2248
2029
  return;
2249
2030
  }
2250
2031
  if ((data === null || data === void 0 ? void 0 : data.type) === "rampkit:goBack") {
2251
- // Only process goBack from the active screen
2032
+ const executeGoBack = () => handleGoBack(i, (data === null || data === void 0 ? void 0 : data.animation) || "fade");
2033
+ // Queue if screen is not active (e.g., on-open actions during preload)
2252
2034
  if (!isScreenActive(i)) {
2253
2035
  if (__DEV__)
2254
- console.log(`[RampKit] Ignoring goBack from inactive screen ${i}`);
2036
+ console.log(`[Rampkit] Queuing goBack from inactive screen ${i}`);
2037
+ queueAction(i, executeGoBack);
2255
2038
  return;
2256
2039
  }
2257
- handleGoBack(i, (data === null || data === void 0 ? void 0 : data.animation) || "fade");
2040
+ executeGoBack();
2258
2041
  return;
2259
2042
  }
2260
2043
  if ((data === null || data === void 0 ? void 0 : data.type) === "rampkit:close") {
2261
2044
  // Track close action for onboarding completion
2262
2045
  try {
2263
- (_f = props.onCloseAction) === null || _f === void 0 ? void 0 : _f.call(props, i, ((_g = props.screens[i]) === null || _g === void 0 ? void 0 : _g.id) || "");
2046
+ (_e = props.onCloseAction) === null || _e === void 0 ? void 0 : _e.call(props, i, ((_f = props.screens[i]) === null || _f === void 0 ? void 0 : _f.id) || "");
2264
2047
  }
2265
2048
  catch (_) { }
2266
2049
  handleRequestClose({ completed: true }); // Mark as completed so abandonment isn't tracked
@@ -2271,18 +2054,20 @@ function Overlay(props) {
2271
2054
  return;
2272
2055
  }
2273
2056
  }
2274
- catch (_m) {
2057
+ catch (_l) {
2275
2058
  // String path
2276
2059
  if (raw === "rampkit:tap" ||
2277
2060
  raw === "next" ||
2278
2061
  raw === "continue") {
2279
- // Only process from the active screen
2062
+ const executeAdvance = () => handleAdvance(i);
2063
+ // Queue if screen is not active (e.g., on-open actions during preload)
2280
2064
  if (!isScreenActive(i)) {
2281
2065
  if (__DEV__)
2282
- console.log(`[RampKit] Ignoring ${raw} from inactive screen ${i}`);
2066
+ console.log(`[Rampkit] Queuing ${raw} from inactive screen ${i}`);
2067
+ queueAction(i, executeAdvance);
2283
2068
  return;
2284
2069
  }
2285
- handleAdvance(i);
2070
+ executeAdvance();
2286
2071
  return;
2287
2072
  }
2288
2073
  if (raw === "rampkit:request-review" || raw === "rampkit:review") {
@@ -2322,7 +2107,7 @@ function Overlay(props) {
2322
2107
  if (raw === "rampkit:onboarding-finished") {
2323
2108
  setOnboardingCompleted(true);
2324
2109
  try {
2325
- (_h = props.onOnboardingFinished) === null || _h === void 0 ? void 0 : _h.call(props, undefined);
2110
+ (_g = props.onOnboardingFinished) === null || _g === void 0 ? void 0 : _g.call(props, undefined);
2326
2111
  }
2327
2112
  catch (_) { }
2328
2113
  handleRequestClose({ completed: true });
@@ -2330,50 +2115,56 @@ function Overlay(props) {
2330
2115
  }
2331
2116
  if (raw === "rampkit:show-paywall") {
2332
2117
  try {
2333
- (_j = props.onShowPaywall) === null || _j === void 0 ? void 0 : _j.call(props);
2118
+ (_h = props.onShowPaywall) === null || _h === void 0 ? void 0 : _h.call(props);
2334
2119
  }
2335
2120
  catch (_) { }
2336
2121
  return;
2337
2122
  }
2338
2123
  if (raw === "rampkit:goBack") {
2339
- // Only process from the active screen
2124
+ const executeGoBack = () => handleGoBack(i);
2125
+ // Queue if screen is not active (e.g., on-open actions during preload)
2340
2126
  if (!isScreenActive(i)) {
2341
2127
  if (__DEV__)
2342
- console.log(`[RampKit] Ignoring goBack (raw) from inactive screen ${i}`);
2128
+ console.log(`[Rampkit] Queuing goBack (raw) from inactive screen ${i}`);
2129
+ queueAction(i, executeGoBack);
2343
2130
  return;
2344
2131
  }
2345
- handleGoBack(i);
2132
+ executeGoBack();
2346
2133
  return;
2347
2134
  }
2348
2135
  if (raw.startsWith("rampkit:navigate:")) {
2349
- // Only process from the active screen
2136
+ const target = raw.slice("rampkit:navigate:".length);
2137
+ const executeNavigate = () => {
2138
+ if (target === "__goBack__") {
2139
+ handleGoBack(i);
2140
+ return;
2141
+ }
2142
+ if (!target || target === "__continue__") {
2143
+ handleAdvance(i);
2144
+ return;
2145
+ }
2146
+ const targetIndex = props.screens.findIndex((s) => s.id === target);
2147
+ if (targetIndex >= 0) {
2148
+ navigateToIndex(targetIndex);
2149
+ }
2150
+ else {
2151
+ handleAdvance(i);
2152
+ }
2153
+ };
2154
+ // Queue if screen is not active (e.g., on-open actions during preload)
2350
2155
  if (!isScreenActive(i)) {
2351
2156
  if (__DEV__)
2352
- console.log(`[RampKit] Ignoring navigate (raw) from inactive screen ${i}`);
2353
- return;
2354
- }
2355
- const target = raw.slice("rampkit:navigate:".length);
2356
- if (target === "__goBack__") {
2357
- handleGoBack(i);
2157
+ console.log(`[Rampkit] Queuing navigate (raw) from inactive screen ${i}`);
2158
+ queueAction(i, executeNavigate);
2358
2159
  return;
2359
2160
  }
2360
- if (!target || target === "__continue__") {
2361
- handleAdvance(i);
2362
- return;
2363
- }
2364
- const targetIndex = props.screens.findIndex((s) => s.id === target);
2365
- if (targetIndex >= 0) {
2366
- navigateToIndex(targetIndex);
2367
- }
2368
- else {
2369
- handleAdvance(i);
2370
- }
2161
+ executeNavigate();
2371
2162
  return;
2372
2163
  }
2373
2164
  if (raw === "rampkit:close") {
2374
2165
  // Track close action for onboarding completion
2375
2166
  try {
2376
- (_k = props.onCloseAction) === null || _k === void 0 ? void 0 : _k.call(props, i, ((_l = props.screens[i]) === null || _l === void 0 ? void 0 : _l.id) || "");
2167
+ (_j = props.onCloseAction) === null || _j === void 0 ? void 0 : _j.call(props, i, ((_k = props.screens[i]) === null || _k === void 0 ? void 0 : _k.id) || "");
2377
2168
  }
2378
2169
  catch (_) { }
2379
2170
  handleRequestClose({ completed: true }); // Mark as completed so abandonment isn't tracked
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rampkit-expo-dev",
3
- "version": "0.0.83",
3
+ "version": "0.0.85",
4
4
  "description": "The Expo SDK for RampKit. Build, test, and personalize app onboardings with instant updates.",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",