rampkit-expo-dev 0.0.42 → 0.0.44
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/build/RampkitOverlay.d.ts +1 -1
- package/build/RampkitOverlay.js +271 -62
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@ import { RampKitContext } 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 injectedButtonAnimations = "\n(function(){\n try {\n if (window.__rkButtonAnimApplied) return true;\n window.__rkButtonAnimApplied = true;\n \n
|
|
5
|
+
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 - very permissive, looks for any clickable-looking element\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 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 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\" or tabindex\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";
|
|
6
6
|
export type ScreenPayload = {
|
|
7
7
|
id: string;
|
|
8
8
|
html: string;
|
package/build/RampkitOverlay.js
CHANGED
|
@@ -175,113 +175,146 @@ exports.injectedVarsHandler = `
|
|
|
175
175
|
`;
|
|
176
176
|
// Button tap animation script - handles spring animations for interactive elements
|
|
177
177
|
// Triggers on touchstart (not click) for immediate feedback
|
|
178
|
+
// Uses inline styles for maximum compatibility
|
|
178
179
|
exports.injectedButtonAnimations = `
|
|
179
180
|
(function(){
|
|
180
181
|
try {
|
|
181
182
|
if (window.__rkButtonAnimApplied) return true;
|
|
182
183
|
window.__rkButtonAnimApplied = true;
|
|
183
184
|
|
|
184
|
-
|
|
185
|
-
var
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
' transform: scale(0.97) !important;' +
|
|
190
|
-
' -webkit-transform: scale(0.97) !important;' +
|
|
191
|
-
' opacity: 0.8 !important;' +
|
|
192
|
-
' transform-origin: center center !important;' +
|
|
193
|
-
' -webkit-transform-origin: center center !important;' +
|
|
194
|
-
' transition: transform 80ms cubic-bezier(0.25, 0.1, 0.25, 1), opacity 80ms cubic-bezier(0.25, 0.1, 0.25, 1), -webkit-transform 80ms cubic-bezier(0.25, 0.1, 0.25, 1) !important;' +
|
|
195
|
-
' -webkit-transition: -webkit-transform 80ms cubic-bezier(0.25, 0.1, 0.25, 1), opacity 80ms cubic-bezier(0.25, 0.1, 0.25, 1) !important;' +
|
|
196
|
-
'}' +
|
|
197
|
-
'*.rk-released, .rk-released, [class*="rk-released"] {' +
|
|
198
|
-
' transform: scale(1) !important;' +
|
|
199
|
-
' -webkit-transform: scale(1) !important;' +
|
|
200
|
-
' opacity: 1 !important;' +
|
|
201
|
-
' transform-origin: center center !important;' +
|
|
202
|
-
' -webkit-transform-origin: center center !important;' +
|
|
203
|
-
' transition: transform 280ms cubic-bezier(0.34, 1.56, 0.64, 1), opacity 280ms cubic-bezier(0.34, 1.56, 0.64, 1), -webkit-transform 280ms cubic-bezier(0.34, 1.56, 0.64, 1) !important;' +
|
|
204
|
-
' -webkit-transition: -webkit-transform 280ms cubic-bezier(0.34, 1.56, 0.64, 1), opacity 280ms cubic-bezier(0.34, 1.56, 0.64, 1) !important;' +
|
|
205
|
-
'}';
|
|
206
|
-
document.head.appendChild(style);
|
|
185
|
+
var pressed = null;
|
|
186
|
+
var pressedOriginalTransform = '';
|
|
187
|
+
var pressedOriginalOpacity = '';
|
|
188
|
+
var pressedOriginalTransition = '';
|
|
189
|
+
var releaseTimer = null;
|
|
207
190
|
|
|
208
|
-
// Find
|
|
191
|
+
// Find interactive element - very permissive, looks for any clickable-looking element
|
|
209
192
|
function findInteractive(el) {
|
|
210
193
|
var current = el;
|
|
211
|
-
for (var i = 0; i <
|
|
212
|
-
if (!current.tagName) { current = current.parentElement; continue; }
|
|
194
|
+
for (var i = 0; i < 20 && current && current !== document.body && current !== document.documentElement; i++) {
|
|
195
|
+
if (!current || !current.tagName) { current = current.parentElement; continue; }
|
|
213
196
|
var tag = current.tagName.toLowerCase();
|
|
214
|
-
|
|
197
|
+
|
|
198
|
+
// Skip tiny elements (likely icons inside buttons)
|
|
199
|
+
var rect = current.getBoundingClientRect();
|
|
200
|
+
if (rect.width < 20 || rect.height < 20) { current = current.parentElement; continue; }
|
|
201
|
+
|
|
202
|
+
// Match standard interactive elements
|
|
215
203
|
if (tag === 'button' || tag === 'a' || tag === 'input' || tag === 'select') return current;
|
|
216
|
-
|
|
204
|
+
|
|
205
|
+
// Match elements with any data attribute containing action/navigate/tap/click
|
|
206
|
+
var attrs = current.attributes;
|
|
207
|
+
if (attrs) {
|
|
208
|
+
for (var j = 0; j < attrs.length; j++) {
|
|
209
|
+
var attrName = attrs[j].name.toLowerCase();
|
|
210
|
+
if (attrName.indexOf('click') !== -1 || attrName.indexOf('tap') !== -1 ||
|
|
211
|
+
attrName.indexOf('action') !== -1 || attrName.indexOf('navigate') !== -1 ||
|
|
212
|
+
attrName.indexOf('press') !== -1) {
|
|
213
|
+
return current;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Match elements with onclick
|
|
217
219
|
if (current.onclick || current.hasAttribute('onclick')) return current;
|
|
218
|
-
|
|
219
|
-
if (current.hasAttribute('data-rampkit-navigate')) return current;
|
|
220
|
-
if (current.hasAttribute('data-rampkit-tap')) return current;
|
|
220
|
+
|
|
221
221
|
// Match elements with role="button" or tabindex
|
|
222
222
|
if (current.getAttribute('role') === 'button') return current;
|
|
223
|
-
|
|
224
|
-
// Match
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
223
|
+
|
|
224
|
+
// Match any element with an ID containing button/btn/cta
|
|
225
|
+
var id = current.id || '';
|
|
226
|
+
if (id && (id.toLowerCase().indexOf('button') !== -1 || id.toLowerCase().indexOf('btn') !== -1 || id.toLowerCase().indexOf('cta') !== -1)) return current;
|
|
227
|
+
|
|
228
|
+
// Match elements with button-like classes
|
|
229
|
+
var className = current.className;
|
|
230
|
+
if (className && typeof className === 'string') {
|
|
231
|
+
var cls = className.toLowerCase();
|
|
232
|
+
if (cls.indexOf('btn') !== -1 || cls.indexOf('button') !== -1 || cls.indexOf('cta') !== -1 ||
|
|
233
|
+
cls.indexOf('clickable') !== -1 || cls.indexOf('tappable') !== -1 || cls.indexOf('pressable') !== -1) {
|
|
234
|
+
return current;
|
|
235
|
+
}
|
|
228
236
|
}
|
|
229
|
-
|
|
237
|
+
|
|
238
|
+
// Match elements with cursor pointer
|
|
230
239
|
try {
|
|
231
240
|
var computed = window.getComputedStyle(current);
|
|
232
241
|
if (computed && computed.cursor === 'pointer') return current;
|
|
233
242
|
} catch(e) {}
|
|
243
|
+
|
|
234
244
|
current = current.parentElement;
|
|
235
245
|
}
|
|
236
246
|
return null;
|
|
237
247
|
}
|
|
238
248
|
|
|
239
|
-
|
|
240
|
-
|
|
249
|
+
function applyPressedStyle(el) {
|
|
250
|
+
if (!el || !el.style) return;
|
|
251
|
+
// Save original styles
|
|
252
|
+
pressedOriginalTransform = el.style.transform || '';
|
|
253
|
+
pressedOriginalOpacity = el.style.opacity || '';
|
|
254
|
+
pressedOriginalTransition = el.style.transition || '';
|
|
255
|
+
// Apply pressed style with inline styles for maximum specificity
|
|
256
|
+
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)';
|
|
257
|
+
el.style.transform = 'scale(0.97)';
|
|
258
|
+
el.style.opacity = '0.8';
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function applyReleasedStyle(el) {
|
|
262
|
+
if (!el || !el.style) return;
|
|
263
|
+
// Apply spring-back animation
|
|
264
|
+
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)';
|
|
265
|
+
el.style.transform = pressedOriginalTransform || 'scale(1)';
|
|
266
|
+
el.style.opacity = pressedOriginalOpacity || '1';
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function resetStyle(el) {
|
|
270
|
+
if (!el || !el.style) return;
|
|
271
|
+
el.style.transform = pressedOriginalTransform;
|
|
272
|
+
el.style.opacity = pressedOriginalOpacity;
|
|
273
|
+
el.style.transition = pressedOriginalTransition;
|
|
274
|
+
}
|
|
241
275
|
|
|
242
|
-
function
|
|
276
|
+
function onTouchStart(e) {
|
|
243
277
|
try {
|
|
244
278
|
var target = findInteractive(e.target);
|
|
245
279
|
if (!target) return;
|
|
246
280
|
if (releaseTimer) { clearTimeout(releaseTimer); releaseTimer = null; }
|
|
247
|
-
target
|
|
248
|
-
target
|
|
281
|
+
if (pressed && pressed !== target) { resetStyle(pressed); }
|
|
282
|
+
applyPressedStyle(target);
|
|
249
283
|
pressed = target;
|
|
250
|
-
} catch(err) {
|
|
284
|
+
} catch(err) {}
|
|
251
285
|
}
|
|
252
286
|
|
|
253
|
-
function
|
|
287
|
+
function onTouchEnd(e) {
|
|
254
288
|
try {
|
|
255
289
|
if (!pressed) return;
|
|
256
290
|
var t = pressed;
|
|
257
|
-
t
|
|
258
|
-
t.classList.add('rk-released');
|
|
291
|
+
applyReleasedStyle(t);
|
|
259
292
|
releaseTimer = setTimeout(function() {
|
|
260
|
-
t
|
|
293
|
+
resetStyle(t);
|
|
261
294
|
releaseTimer = null;
|
|
262
295
|
}, 300);
|
|
263
296
|
pressed = null;
|
|
264
297
|
} catch(err) {}
|
|
265
298
|
}
|
|
266
299
|
|
|
267
|
-
function
|
|
300
|
+
function onTouchCancel(e) {
|
|
268
301
|
try {
|
|
269
302
|
if (!pressed) return;
|
|
270
|
-
|
|
303
|
+
resetStyle(pressed);
|
|
271
304
|
pressed = null;
|
|
272
305
|
if (releaseTimer) { clearTimeout(releaseTimer); releaseTimer = null; }
|
|
273
306
|
} catch(err) {}
|
|
274
307
|
}
|
|
275
308
|
|
|
276
|
-
//
|
|
277
|
-
document.addEventListener('touchstart',
|
|
278
|
-
document.addEventListener('touchend',
|
|
279
|
-
document.addEventListener('touchcancel',
|
|
280
|
-
|
|
281
|
-
document.addEventListener('
|
|
309
|
+
// Use capture phase for immediate response before any other handlers
|
|
310
|
+
document.addEventListener('touchstart', onTouchStart, { passive: true, capture: true });
|
|
311
|
+
document.addEventListener('touchend', onTouchEnd, { passive: true, capture: true });
|
|
312
|
+
document.addEventListener('touchcancel', onTouchCancel, { passive: true, capture: true });
|
|
313
|
+
// Mouse events for testing
|
|
314
|
+
document.addEventListener('mousedown', onTouchStart, { passive: true, capture: true });
|
|
315
|
+
document.addEventListener('mouseup', onTouchEnd, { passive: true, capture: true });
|
|
282
316
|
|
|
283
|
-
|
|
284
|
-
} catch (err) { console.log('[RK] Button anim error:', err); }
|
|
317
|
+
} catch (err) {}
|
|
285
318
|
true;
|
|
286
319
|
})();
|
|
287
320
|
`;
|
|
@@ -384,16 +417,177 @@ function preloadRampkitOverlay(opts) {
|
|
|
384
417
|
opacity: 0,
|
|
385
418
|
top: -1000,
|
|
386
419
|
left: -1000,
|
|
387
|
-
}, children: (0, jsx_runtime_1.jsx)(react_native_webview_1.WebView, { originWhitelist: ["*"], source: { html: docs[0] || "<html></html>" }, injectedJavaScriptBeforeContentLoaded: exports.injectedHardening, injectedJavaScript: exports.injectedNoSelect + exports.injectedVarsHandler + exports.injectedButtonAnimations, automaticallyAdjustContentInsets: false, contentInsetAdjustmentBehavior: "never", bounces: false, scrollEnabled: false, allowsInlineMediaPlayback: true, mediaPlaybackRequiresUserAction: false, cacheEnabled: true, hideKeyboardAccessoryView: true }) }));
|
|
420
|
+
}, children: (0, jsx_runtime_1.jsx)(react_native_webview_1.WebView, { originWhitelist: ["*"], source: { html: docs[0] || "<html></html>" }, injectedJavaScriptBeforeContentLoaded: exports.injectedHardening + exports.injectedButtonAnimations, injectedJavaScript: exports.injectedNoSelect + exports.injectedVarsHandler + exports.injectedButtonAnimations, automaticallyAdjustContentInsets: false, contentInsetAdjustmentBehavior: "never", bounces: false, scrollEnabled: false, allowsInlineMediaPlayback: true, mediaPlaybackRequiresUserAction: false, cacheEnabled: true, hideKeyboardAccessoryView: true }) }));
|
|
388
421
|
preloadSibling = new react_native_root_siblings_1.default((0, jsx_runtime_1.jsx)(HiddenPreloader, {}));
|
|
389
422
|
}
|
|
390
423
|
catch (e) {
|
|
391
424
|
// best-effort preloading; ignore errors
|
|
392
425
|
}
|
|
393
426
|
}
|
|
427
|
+
/**
|
|
428
|
+
* Evaluate a comparison condition against variables
|
|
429
|
+
* Supports: ==, !=, >, <, >=, <=, and truthy checks
|
|
430
|
+
*/
|
|
431
|
+
function evaluateCondition(condition, vars) {
|
|
432
|
+
condition = condition.trim();
|
|
433
|
+
// Match comparison operators: ==, !=, >=, <=, >, <
|
|
434
|
+
const comparisonMatch = condition.match(/^([A-Za-z_][A-Za-z0-9_.]*)\s*(==|!=|>=|<=|>|<)\s*(.+)$/);
|
|
435
|
+
if (comparisonMatch) {
|
|
436
|
+
const [, varName, operator, rawRight] = comparisonMatch;
|
|
437
|
+
const leftValue = vars.hasOwnProperty(varName) ? vars[varName] : undefined;
|
|
438
|
+
let rightValue = rawRight.trim();
|
|
439
|
+
// Parse right side - could be a quoted string or a number or a variable
|
|
440
|
+
if ((rightValue.startsWith('"') && rightValue.endsWith('"')) ||
|
|
441
|
+
(rightValue.startsWith("'") && rightValue.endsWith("'"))) {
|
|
442
|
+
// Quoted string literal
|
|
443
|
+
rightValue = rightValue.slice(1, -1);
|
|
444
|
+
}
|
|
445
|
+
else if (!isNaN(Number(rightValue))) {
|
|
446
|
+
// Numeric literal
|
|
447
|
+
rightValue = Number(rightValue);
|
|
448
|
+
}
|
|
449
|
+
else if (rightValue === "true") {
|
|
450
|
+
rightValue = true;
|
|
451
|
+
}
|
|
452
|
+
else if (rightValue === "false") {
|
|
453
|
+
rightValue = false;
|
|
454
|
+
}
|
|
455
|
+
else if (rightValue === "null") {
|
|
456
|
+
rightValue = null;
|
|
457
|
+
}
|
|
458
|
+
else if (vars.hasOwnProperty(rightValue)) {
|
|
459
|
+
// Variable reference
|
|
460
|
+
rightValue = vars[rightValue];
|
|
461
|
+
}
|
|
462
|
+
// Perform comparison
|
|
463
|
+
switch (operator) {
|
|
464
|
+
case "==":
|
|
465
|
+
return leftValue == rightValue;
|
|
466
|
+
case "!=":
|
|
467
|
+
return leftValue != rightValue;
|
|
468
|
+
case ">":
|
|
469
|
+
return Number(leftValue) > Number(rightValue);
|
|
470
|
+
case "<":
|
|
471
|
+
return Number(leftValue) < Number(rightValue);
|
|
472
|
+
case ">=":
|
|
473
|
+
return Number(leftValue) >= Number(rightValue);
|
|
474
|
+
case "<=":
|
|
475
|
+
return Number(leftValue) <= Number(rightValue);
|
|
476
|
+
default:
|
|
477
|
+
return false;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
// Truthy check - just the variable name
|
|
481
|
+
const varName = condition.trim();
|
|
482
|
+
if (vars.hasOwnProperty(varName)) {
|
|
483
|
+
const value = vars[varName];
|
|
484
|
+
// Consider empty string as falsy
|
|
485
|
+
if (value === "")
|
|
486
|
+
return false;
|
|
487
|
+
return !!value;
|
|
488
|
+
}
|
|
489
|
+
// Unknown variable - treat as falsy
|
|
490
|
+
return false;
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* Parse a ternary value (the part after ? or after :)
|
|
494
|
+
* Returns the resolved value, handling both quoted strings and variable references
|
|
495
|
+
*/
|
|
496
|
+
function parseTernaryValue(value, vars) {
|
|
497
|
+
value = value.trim();
|
|
498
|
+
// Check if it's a quoted string
|
|
499
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
500
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
501
|
+
return value.slice(1, -1);
|
|
502
|
+
}
|
|
503
|
+
// Otherwise treat as a variable reference
|
|
504
|
+
if (vars.hasOwnProperty(value)) {
|
|
505
|
+
const varValue = vars[value];
|
|
506
|
+
if (varValue === undefined || varValue === null)
|
|
507
|
+
return "";
|
|
508
|
+
if (typeof varValue === "boolean")
|
|
509
|
+
return varValue ? "true" : "false";
|
|
510
|
+
if (typeof varValue === "object")
|
|
511
|
+
return JSON.stringify(varValue);
|
|
512
|
+
return String(varValue);
|
|
513
|
+
}
|
|
514
|
+
// Return as-is if not found (could be a literal like a number)
|
|
515
|
+
return value;
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Parse a ternary expression and find the colon that separates true/false values
|
|
519
|
+
* Handles nested quotes properly
|
|
520
|
+
*/
|
|
521
|
+
function splitTernary(expr) {
|
|
522
|
+
// Find the ? that starts the ternary
|
|
523
|
+
let questionIdx = -1;
|
|
524
|
+
let inQuote = false;
|
|
525
|
+
let quoteChar = "";
|
|
526
|
+
for (let i = 0; i < expr.length; i++) {
|
|
527
|
+
const char = expr[i];
|
|
528
|
+
const prevChar = i > 0 ? expr[i - 1] : "";
|
|
529
|
+
if ((char === '"' || char === "'") && prevChar !== "\\") {
|
|
530
|
+
if (!inQuote) {
|
|
531
|
+
inQuote = true;
|
|
532
|
+
quoteChar = char;
|
|
533
|
+
}
|
|
534
|
+
else if (char === quoteChar) {
|
|
535
|
+
inQuote = false;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
if (!inQuote && char === "?") {
|
|
539
|
+
questionIdx = i;
|
|
540
|
+
break;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
if (questionIdx === -1)
|
|
544
|
+
return null;
|
|
545
|
+
const condition = expr.slice(0, questionIdx).trim();
|
|
546
|
+
const rest = expr.slice(questionIdx + 1);
|
|
547
|
+
// Find the : that separates true/false values
|
|
548
|
+
let colonIdx = -1;
|
|
549
|
+
inQuote = false;
|
|
550
|
+
quoteChar = "";
|
|
551
|
+
for (let i = 0; i < rest.length; i++) {
|
|
552
|
+
const char = rest[i];
|
|
553
|
+
const prevChar = i > 0 ? rest[i - 1] : "";
|
|
554
|
+
if ((char === '"' || char === "'") && prevChar !== "\\") {
|
|
555
|
+
if (!inQuote) {
|
|
556
|
+
inQuote = true;
|
|
557
|
+
quoteChar = char;
|
|
558
|
+
}
|
|
559
|
+
else if (char === quoteChar) {
|
|
560
|
+
inQuote = false;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
if (!inQuote && char === ":") {
|
|
564
|
+
colonIdx = i;
|
|
565
|
+
break;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
if (colonIdx === -1)
|
|
569
|
+
return null;
|
|
570
|
+
const trueValue = rest.slice(0, colonIdx).trim();
|
|
571
|
+
const falseValue = rest.slice(colonIdx + 1).trim();
|
|
572
|
+
return { condition, trueValue, falseValue };
|
|
573
|
+
}
|
|
394
574
|
/**
|
|
395
575
|
* Resolve device/user templates in a string
|
|
396
|
-
*
|
|
576
|
+
* Supports both simple variables ${varName} and conditional ternary expressions
|
|
577
|
+
* ${condition ? "trueValue" : "falseValue"}
|
|
578
|
+
*
|
|
579
|
+
* Supported operators in conditions:
|
|
580
|
+
* - == (equals)
|
|
581
|
+
* - != (not equals)
|
|
582
|
+
* - > (greater than)
|
|
583
|
+
* - < (less than)
|
|
584
|
+
* - >= (greater or equal)
|
|
585
|
+
* - <= (less or equal)
|
|
586
|
+
* - Truthy check (just variable name)
|
|
587
|
+
*
|
|
588
|
+
* Values can be:
|
|
589
|
+
* - Quoted strings: "hello" or 'hello'
|
|
590
|
+
* - Variable references: username
|
|
397
591
|
*/
|
|
398
592
|
function resolveContextTemplates(text, context) {
|
|
399
593
|
if (!text || !text.includes("${"))
|
|
@@ -413,8 +607,23 @@ function resolveContextTemplates(text, context) {
|
|
|
413
607
|
});
|
|
414
608
|
}
|
|
415
609
|
console.log("[RampKit] Resolving templates with vars:", JSON.stringify(vars));
|
|
416
|
-
//
|
|
417
|
-
|
|
610
|
+
// Match ${...} expressions - use a more permissive regex to capture full expressions
|
|
611
|
+
// including ternary operators with quotes
|
|
612
|
+
return text.replace(/\$\{([^}]+)\}/g, (match, innerExpr) => {
|
|
613
|
+
const expr = innerExpr.trim();
|
|
614
|
+
// Check if this is a ternary expression
|
|
615
|
+
const ternary = splitTernary(expr);
|
|
616
|
+
if (ternary) {
|
|
617
|
+
const { condition, trueValue, falseValue } = ternary;
|
|
618
|
+
const result = evaluateCondition(condition, vars);
|
|
619
|
+
const value = result
|
|
620
|
+
? parseTernaryValue(trueValue, vars)
|
|
621
|
+
: parseTernaryValue(falseValue, vars);
|
|
622
|
+
console.log(`[RampKit] Ternary: ${condition} ? ${trueValue} : ${falseValue} => ${result} => "${value}"`);
|
|
623
|
+
return value;
|
|
624
|
+
}
|
|
625
|
+
// Simple variable substitution
|
|
626
|
+
const varName = expr;
|
|
418
627
|
if (vars.hasOwnProperty(varName)) {
|
|
419
628
|
const value = vars[varName];
|
|
420
629
|
console.log(`[RampKit] Replacing ${match} with:`, value);
|
|
@@ -939,7 +1148,7 @@ function Overlay(props) {
|
|
|
939
1148
|
opacity: pagerOpacity,
|
|
940
1149
|
transform: [{ translateX: pagerTranslateX }],
|
|
941
1150
|
},
|
|
942
|
-
], children: (0, jsx_runtime_1.jsx)(react_native_pager_view_1.default, { ref: pagerRef, style: react_native_1.StyleSheet.absoluteFill, scrollEnabled: false, initialPage: 0, onPageSelected: onPageSelected, offscreenPageLimit: props.screens.length, overScrollMode: "never", children: docs.map((doc, i) => ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.page, renderToHardwareTextureAndroid: true, children: (0, jsx_runtime_1.jsx)(react_native_webview_1.WebView, { ref: (r) => (webviewsRef.current[i] = r), style: styles.webview, originWhitelist: ["*"], source: { html: doc }, injectedJavaScriptBeforeContentLoaded: exports.injectedHardening, injectedJavaScript: exports.injectedNoSelect + exports.injectedVarsHandler + exports.injectedButtonAnimations, automaticallyAdjustContentInsets: false, contentInsetAdjustmentBehavior: "never", bounces: false, scrollEnabled: false, overScrollMode: "never", scalesPageToFit: false, showsHorizontalScrollIndicator: false, dataDetectorTypes: "none", allowsLinkPreview: false, allowsInlineMediaPlayback: true, mediaPlaybackRequiresUserAction: false, cacheEnabled: true, javaScriptEnabled: true, domStorageEnabled: true, hideKeyboardAccessoryView: true, onLoadEnd: () => {
|
|
1151
|
+
], children: (0, jsx_runtime_1.jsx)(react_native_pager_view_1.default, { ref: pagerRef, style: react_native_1.StyleSheet.absoluteFill, scrollEnabled: false, initialPage: 0, onPageSelected: onPageSelected, offscreenPageLimit: props.screens.length, overScrollMode: "never", children: docs.map((doc, i) => ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.page, renderToHardwareTextureAndroid: true, children: (0, jsx_runtime_1.jsx)(react_native_webview_1.WebView, { ref: (r) => (webviewsRef.current[i] = r), style: styles.webview, originWhitelist: ["*"], source: { html: doc }, injectedJavaScriptBeforeContentLoaded: exports.injectedHardening + exports.injectedButtonAnimations, injectedJavaScript: exports.injectedNoSelect + exports.injectedVarsHandler + exports.injectedButtonAnimations, automaticallyAdjustContentInsets: false, contentInsetAdjustmentBehavior: "never", bounces: false, scrollEnabled: false, overScrollMode: "never", scalesPageToFit: false, showsHorizontalScrollIndicator: false, dataDetectorTypes: "none", allowsLinkPreview: false, allowsInlineMediaPlayback: true, mediaPlaybackRequiresUserAction: false, cacheEnabled: true, javaScriptEnabled: true, domStorageEnabled: true, hideKeyboardAccessoryView: true, onLoadEnd: () => {
|
|
943
1152
|
setLoadedCount((c) => c + 1);
|
|
944
1153
|
if (i === 0) {
|
|
945
1154
|
setFirstPageLoaded(true);
|
package/package.json
CHANGED