@thewhateverapp/tile-sdk 0.12.12 → 0.12.14
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.
|
@@ -59,6 +59,7 @@ export declare class TileBridge {
|
|
|
59
59
|
private tokenExpiresAt;
|
|
60
60
|
private keyboardState;
|
|
61
61
|
private visibilityState;
|
|
62
|
+
private receivedVisibilityMessage;
|
|
62
63
|
private mediaOriginalMutedState;
|
|
63
64
|
private trackedAudioContexts;
|
|
64
65
|
private audioContextPatched;
|
|
@@ -112,6 +113,25 @@ export declare class TileBridge {
|
|
|
112
113
|
* Now includes muted state for atomic updates to prevent race conditions
|
|
113
114
|
*/
|
|
114
115
|
private handleVisibility;
|
|
116
|
+
/**
|
|
117
|
+
* ROBUST: Monkey-patch AudioContext to auto-intercept ALL new AudioContext() calls
|
|
118
|
+
* This catches WASM/Emscripten audio without tiles needing to call registerAudioContext()
|
|
119
|
+
*/
|
|
120
|
+
private patchAudioContext;
|
|
121
|
+
/**
|
|
122
|
+
* ROBUST: MutationObserver to watch for dynamically added media elements
|
|
123
|
+
* Auto-mutes any <video> or <audio> elements added while tile is offscreen
|
|
124
|
+
*/
|
|
125
|
+
private setupMediaObserver;
|
|
126
|
+
/**
|
|
127
|
+
* SELF-DETECTION: Use Page Visibility API as fallback for detecting when
|
|
128
|
+
* the WebView/tab is hidden (app backgrounded, tab switched, screen locked)
|
|
129
|
+
*
|
|
130
|
+
* This works independently of the parent sending visibility messages.
|
|
131
|
+
* NOTE: This does NOT detect when the WebView is scrolled offscreen in a
|
|
132
|
+
* TikTok-style feed - that requires the native app to send visibility messages.
|
|
133
|
+
*/
|
|
134
|
+
private setupPageVisibilityFallback;
|
|
115
135
|
/**
|
|
116
136
|
* MANDATORY: Mute all audio sources when tile is offscreen
|
|
117
137
|
* - Mutes all <video> and <audio> elements
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TileBridge.d.ts","sourceRoot":"","sources":["../../src/bridge/TileBridge.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,iGAAiG;IACjG,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAGD,KAAK,UAAU,GAAG;IAChB,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,IAAI,EAAE,MAAM,IAAI,CAAC;CAClB,CAAC;AAEF,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAA2B;IACzC,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,gBAAgB,CAAmD;IAC3E,OAAO,CAAC,aAAa,CAAoD;IACzE,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,MAAM,CAA2B;IAGzC,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,cAAc,CAAqB;IAG3C,OAAO,CAAC,aAAa,CAAgD;
|
|
1
|
+
{"version":3,"file":"TileBridge.d.ts","sourceRoot":"","sources":["../../src/bridge/TileBridge.ts"],"names":[],"mappings":"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"}
|
|
@@ -17,7 +17,11 @@ export class TileBridge {
|
|
|
17
17
|
this.keyboardState = { visible: false, height: 0 };
|
|
18
18
|
// Visibility state from parent (for TikTok-style feeds)
|
|
19
19
|
// Includes muted state for atomic updates to prevent race conditions
|
|
20
|
-
|
|
20
|
+
// FAIL-SAFE: Default to muted=true until parent explicitly says otherwise
|
|
21
|
+
// This prevents audio leakage if visibility message is delayed or never arrives
|
|
22
|
+
this.visibilityState = { visible: true, muted: true };
|
|
23
|
+
// Track whether we've received at least one visibility message from parent
|
|
24
|
+
this.receivedVisibilityMessage = false;
|
|
21
25
|
// Store original muted state of media elements to restore when unmuting
|
|
22
26
|
this.mediaOriginalMutedState = new WeakMap();
|
|
23
27
|
// Track all AudioContext instances for suspension (catches Emscripten/WASM audio)
|
|
@@ -86,6 +90,12 @@ export class TileBridge {
|
|
|
86
90
|
this.sendToParent({ type: 'tile:ready' });
|
|
87
91
|
// Initialize keyboard detection (Plan A: VisualViewport + Plan B: Input Focus)
|
|
88
92
|
this.initializeKeyboardDetection();
|
|
93
|
+
// ROBUST AUDIO MUTING: Auto-intercept all audio sources
|
|
94
|
+
this.patchAudioContext();
|
|
95
|
+
this.setupMediaObserver();
|
|
96
|
+
// SELF-DETECTION: Use Page Visibility API as fallback for detecting when
|
|
97
|
+
// the WebView/tab is hidden (app backgrounded, tab switched, screen locked)
|
|
98
|
+
this.setupPageVisibilityFallback();
|
|
89
99
|
}
|
|
90
100
|
/**
|
|
91
101
|
* Initialize keyboard detection using VisualViewport API and input focus tracking
|
|
@@ -402,6 +412,9 @@ export class TileBridge {
|
|
|
402
412
|
console.warn('[TileBridge] Invalid visibility payload received');
|
|
403
413
|
return;
|
|
404
414
|
}
|
|
415
|
+
// Mark that we've received at least one visibility message from parent
|
|
416
|
+
// This means parent is controlling visibility, so we defer to their mute state
|
|
417
|
+
this.receivedVisibilityMessage = true;
|
|
405
418
|
const wasVisible = this.visibilityState.visible;
|
|
406
419
|
const wasMuted = this.visibilityState.muted;
|
|
407
420
|
// Update visibility and muted state atomically
|
|
@@ -424,6 +437,179 @@ export class TileBridge {
|
|
|
424
437
|
this.emitEvent('visibility:update', this.visibilityState);
|
|
425
438
|
}
|
|
426
439
|
}
|
|
440
|
+
/**
|
|
441
|
+
* ROBUST: Monkey-patch AudioContext to auto-intercept ALL new AudioContext() calls
|
|
442
|
+
* This catches WASM/Emscripten audio without tiles needing to call registerAudioContext()
|
|
443
|
+
*/
|
|
444
|
+
patchAudioContext() {
|
|
445
|
+
if (typeof window === 'undefined' || this.audioContextPatched)
|
|
446
|
+
return;
|
|
447
|
+
this.audioContextPatched = true;
|
|
448
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
449
|
+
const bridge = this;
|
|
450
|
+
const OriginalAudioContext = window.AudioContext || window.webkitAudioContext;
|
|
451
|
+
if (!OriginalAudioContext) {
|
|
452
|
+
console.log('[TileBridge] AudioContext not available in this environment');
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
// Create a patched constructor that auto-registers all instances
|
|
456
|
+
function PatchedAudioContext(options) {
|
|
457
|
+
// Call original constructor
|
|
458
|
+
const ctx = new OriginalAudioContext(options);
|
|
459
|
+
// Auto-register this context
|
|
460
|
+
bridge.trackedAudioContexts.add(ctx);
|
|
461
|
+
console.log(`[TileBridge] 🎵 AudioContext auto-intercepted (${bridge.trackedAudioContexts.size} total)`);
|
|
462
|
+
// If tile is currently muted, suspend immediately
|
|
463
|
+
if (bridge.visibilityState.muted && ctx.state === 'running') {
|
|
464
|
+
console.log('[TileBridge] 🔇 New AudioContext auto-suspended (tile is muted)');
|
|
465
|
+
ctx.suspend().catch(() => { });
|
|
466
|
+
}
|
|
467
|
+
return ctx;
|
|
468
|
+
}
|
|
469
|
+
// Copy prototype and static properties
|
|
470
|
+
PatchedAudioContext.prototype = OriginalAudioContext.prototype;
|
|
471
|
+
Object.setPrototypeOf(PatchedAudioContext, OriginalAudioContext);
|
|
472
|
+
// Replace global AudioContext
|
|
473
|
+
window.AudioContext = PatchedAudioContext;
|
|
474
|
+
if (window.webkitAudioContext) {
|
|
475
|
+
window.webkitAudioContext = PatchedAudioContext;
|
|
476
|
+
}
|
|
477
|
+
console.log('[TileBridge] 🔧 AudioContext patched for auto-interception');
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* ROBUST: MutationObserver to watch for dynamically added media elements
|
|
481
|
+
* Auto-mutes any <video> or <audio> elements added while tile is offscreen
|
|
482
|
+
*/
|
|
483
|
+
setupMediaObserver() {
|
|
484
|
+
if (typeof document === 'undefined' || typeof MutationObserver === 'undefined')
|
|
485
|
+
return;
|
|
486
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
487
|
+
const bridge = this;
|
|
488
|
+
const observer = new MutationObserver((mutations) => {
|
|
489
|
+
// Only process if we're currently muted
|
|
490
|
+
if (!bridge.visibilityState.muted)
|
|
491
|
+
return;
|
|
492
|
+
mutations.forEach((mutation) => {
|
|
493
|
+
mutation.addedNodes.forEach((node) => {
|
|
494
|
+
// Check if the added node is a media element
|
|
495
|
+
if (node instanceof HTMLMediaElement) {
|
|
496
|
+
if (!node.muted) {
|
|
497
|
+
bridge.mediaOriginalMutedState.set(node, node.muted);
|
|
498
|
+
node.muted = true;
|
|
499
|
+
console.log('[TileBridge] 🔇 Auto-muted dynamically added media element');
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
// Also check children of added nodes
|
|
503
|
+
if (node instanceof Element) {
|
|
504
|
+
const mediaElements = node.querySelectorAll('video, audio');
|
|
505
|
+
mediaElements.forEach((element) => {
|
|
506
|
+
const media = element;
|
|
507
|
+
if (!media.muted) {
|
|
508
|
+
bridge.mediaOriginalMutedState.set(media, media.muted);
|
|
509
|
+
media.muted = true;
|
|
510
|
+
console.log('[TileBridge] 🔇 Auto-muted dynamically added media element (child)');
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
});
|
|
515
|
+
});
|
|
516
|
+
});
|
|
517
|
+
// Start observing the entire document for added nodes
|
|
518
|
+
observer.observe(document.documentElement, {
|
|
519
|
+
childList: true,
|
|
520
|
+
subtree: true,
|
|
521
|
+
});
|
|
522
|
+
console.log('[TileBridge] 👁️ MutationObserver active for dynamic media elements');
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* SELF-DETECTION: Use Page Visibility API as fallback for detecting when
|
|
526
|
+
* the WebView/tab is hidden (app backgrounded, tab switched, screen locked)
|
|
527
|
+
*
|
|
528
|
+
* This works independently of the parent sending visibility messages.
|
|
529
|
+
* NOTE: This does NOT detect when the WebView is scrolled offscreen in a
|
|
530
|
+
* TikTok-style feed - that requires the native app to send visibility messages.
|
|
531
|
+
*/
|
|
532
|
+
setupPageVisibilityFallback() {
|
|
533
|
+
if (typeof document === 'undefined')
|
|
534
|
+
return;
|
|
535
|
+
// Check initial state - if page is hidden at startup, ensure we're muted
|
|
536
|
+
if (document.hidden) {
|
|
537
|
+
console.log('[TileBridge] 📱 Page hidden at startup - ensuring muted');
|
|
538
|
+
this.visibilityState.muted = true;
|
|
539
|
+
this.muteAllMedia();
|
|
540
|
+
}
|
|
541
|
+
// STANDALONE TILE DETECTION:
|
|
542
|
+
// If NOT in an iframe, this is a standalone tile (direct browser access).
|
|
543
|
+
// Standalone tiles should manage their own audio based on Page Visibility only.
|
|
544
|
+
// We unmute after a short delay if page is visible and we're not in iframe.
|
|
545
|
+
if (!this.isInIframe()) {
|
|
546
|
+
console.log('[TileBridge] 📱 Standalone tile detected (not in iframe)');
|
|
547
|
+
if (!document.hidden) {
|
|
548
|
+
// Page is visible and we're standalone - unmute after brief delay
|
|
549
|
+
// to ensure initialization is complete
|
|
550
|
+
setTimeout(() => {
|
|
551
|
+
if (!this.receivedVisibilityMessage && !document.hidden) {
|
|
552
|
+
console.log('[TileBridge] 📱 Standalone tile visible - unmuting');
|
|
553
|
+
this.visibilityState.muted = false;
|
|
554
|
+
this.unmuteAllMedia();
|
|
555
|
+
}
|
|
556
|
+
}, 100);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
else {
|
|
560
|
+
// IN IFRAME: Start muted and wait for parent's visibility message
|
|
561
|
+
// If parent doesn't send within 3 seconds AND page is visible, assume
|
|
562
|
+
// parent doesn't implement visibility protocol (web parent, not mobile)
|
|
563
|
+
setTimeout(() => {
|
|
564
|
+
if (!this.receivedVisibilityMessage && !document.hidden) {
|
|
565
|
+
console.log('[TileBridge] 📱 No visibility message received, page visible - unmuting (web parent?)');
|
|
566
|
+
this.visibilityState.muted = false;
|
|
567
|
+
this.unmuteAllMedia();
|
|
568
|
+
}
|
|
569
|
+
}, 3000);
|
|
570
|
+
}
|
|
571
|
+
// Listen for visibility changes
|
|
572
|
+
document.addEventListener('visibilitychange', () => {
|
|
573
|
+
const isHidden = document.hidden;
|
|
574
|
+
console.log(`[TileBridge] 📱 Page visibility changed: ${isHidden ? 'hidden' : 'visible'}`);
|
|
575
|
+
if (isHidden) {
|
|
576
|
+
// Page is hidden (app backgrounded, tab switched, screen locked)
|
|
577
|
+
// ALWAYS mute - this is a hard requirement
|
|
578
|
+
this.visibilityState.muted = true;
|
|
579
|
+
this.muteAllMedia();
|
|
580
|
+
}
|
|
581
|
+
else {
|
|
582
|
+
// Page is visible again
|
|
583
|
+
// Only unmute if parent has explicitly said we're not muted
|
|
584
|
+
// OR if we've never received a visibility message (standalone tile)
|
|
585
|
+
if (!this.receivedVisibilityMessage || !this.visibilityState.muted) {
|
|
586
|
+
// For standalone tiles (no parent visibility control), restore audio
|
|
587
|
+
console.log('[TileBridge] 📱 Page visible - restoring audio');
|
|
588
|
+
this.visibilityState.muted = false;
|
|
589
|
+
this.unmuteAllMedia();
|
|
590
|
+
}
|
|
591
|
+
else {
|
|
592
|
+
// Parent is controlling visibility - respect their muted state
|
|
593
|
+
console.log('[TileBridge] 📱 Page visible but parent controls mute state');
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
});
|
|
597
|
+
// Also handle window blur/focus as additional fallback
|
|
598
|
+
window.addEventListener('blur', () => {
|
|
599
|
+
console.log('[TileBridge] 📱 Window blur - muting audio');
|
|
600
|
+
this.visibilityState.muted = true;
|
|
601
|
+
this.muteAllMedia();
|
|
602
|
+
});
|
|
603
|
+
window.addEventListener('focus', () => {
|
|
604
|
+
// Only restore if page is visible and parent hasn't explicitly muted
|
|
605
|
+
if (!document.hidden && (!this.receivedVisibilityMessage || !this.visibilityState.muted)) {
|
|
606
|
+
console.log('[TileBridge] 📱 Window focus - restoring audio');
|
|
607
|
+
this.visibilityState.muted = false;
|
|
608
|
+
this.unmuteAllMedia();
|
|
609
|
+
}
|
|
610
|
+
});
|
|
611
|
+
console.log('[TileBridge] 📱 Page Visibility fallback active');
|
|
612
|
+
}
|
|
427
613
|
/**
|
|
428
614
|
* MANDATORY: Mute all audio sources when tile is offscreen
|
|
429
615
|
* - Mutes all <video> and <audio> elements
|