reaper-arcade-hub 2.9.1 → 2.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.html CHANGED
@@ -38,7 +38,7 @@
38
38
  <div class="loader-brand-title">reaper</div>
39
39
  <div class="loader-badge-tag">
40
40
  <span class="badge-dot-live"></span>
41
- <span>V2.9.1 ZERO-LAG ENGINE</span> • 788 GAMES • 60 FPS
41
+ <span>V2.9.2 ZERO-LAG ENGINE</span> • 788 GAMES • LOCAL SAVE ENABLED
42
42
  </div>
43
43
 
44
44
  <!-- Live Terminal Kernel Diagnostics -->
package/js/app.js CHANGED
@@ -1,6 +1,6 @@
1
- // REAPER HUB - Core Engine (v2.9.1 100% Zero-Lag 60 FPS Engine & Auto about:blank Isolation)
2
- window.REAPER_VERSION = "2.9.1";
3
- window.REAPER_LATEST_URL = "https://unpkg.com/reaper-arcade-hub@2.9.1/main/index.html";
1
+ // REAPER HUB - Core Engine (v2.9.2 Persistent Local Save Engine & 60 FPS about:blank Isolation)
2
+ window.REAPER_VERSION = "2.9.2";
3
+ window.REAPER_LATEST_URL = "https://unpkg.com/reaper-arcade-hub@2.9.2/main/index.html";
4
4
 
5
5
  function _isMasterOwnerToken(k) {
6
6
  if (!k) return false;
@@ -283,7 +283,19 @@ class ReaperHubApp {
283
283
  }
284
284
  }
285
285
 
286
- // 11. Auto-show Update Logs on visit if new version
286
+ // 11. Realtime Persistent Game Save Synchronizer for about:blank tabs
287
+ window.addEventListener('message', (e) => {
288
+ if (e.data && e.data.type === 'REAPER_SAVE_SYNC' && e.data.gameId) {
289
+ try {
290
+ const key = 'reaper_save_' + e.data.gameId;
291
+ const current = JSON.parse(localStorage.getItem(key) || '{}');
292
+ Object.assign(current, e.data.data);
293
+ localStorage.setItem(key, JSON.stringify(current));
294
+ } catch(err) {}
295
+ }
296
+ });
297
+
298
+ // 12. Auto-show Update Logs on visit if new version
287
299
  const lastSeenVersion = localStorage.getItem('reaper_last_seen_version');
288
300
  if (lastSeenVersion !== '1.0.9') {
289
301
  setTimeout(() => {
@@ -2588,7 +2600,7 @@ Query: **"${prompt}"**
2588
2600
  }
2589
2601
  }
2590
2602
 
2591
- cleanAndOptimizeGameHTML(html) {
2603
+ cleanAndOptimizeGameHTML(html, gameId) {
2592
2604
  if (!html || typeof html !== 'string') return '';
2593
2605
  let cleaned = html;
2594
2606
 
@@ -2597,8 +2609,10 @@ Query: **"${prompt}"**
2597
2609
  cleaned = cleaned.replace(/<div id="sidebarad2"[\s\S]*?<\/div>\s*<\/div>/gi, '');
2598
2610
  cleaned = cleaned.replace(/<style>[\s\S]*?#sidebarad1[\s\S]*?<\/style>/gi, '');
2599
2611
 
2600
- // 2. Inject high-performance responsive scaling, centering & click-focus engine
2601
- const injectStyles = `
2612
+ const safeGameId = String(gameId || (this.currentGame ? this.currentGame.id : 'default'));
2613
+
2614
+ // 2. Inject high-performance responsive scaling & Universal Persistent Local Save Engine
2615
+ const injectStylesAndSave = `
2602
2616
  <style id="reaper-game-enhancer">
2603
2617
  * { box-sizing: border-box; }
2604
2618
  html, body {
@@ -2632,22 +2646,158 @@ Query: **"${prompt}"**
2632
2646
  z-index: -9999 !important;
2633
2647
  }
2634
2648
  </style>
2635
- <script>
2636
- // Neutralize ad popups & focus game canvas
2637
- window.open = function() { return null; };
2638
- window.addEventListener('load', function() {
2639
- const c = document.querySelector('canvas') || document.querySelector('#game');
2640
- if (c && c.focus) c.focus();
2641
- });
2649
+ <script id="reaper-save-engine">
2650
+ (function() {
2651
+ var GAME_ID = "` + safeGameId + `";
2652
+ var STORAGE_KEY = "reaper_save_" + GAME_ID;
2653
+ var memoryStore = {};
2654
+
2655
+ // Initial State Restoration from Parent Window or Opener
2656
+ try {
2657
+ if (window.parent && window.parent.REAPER_INITIAL_SAVE) {
2658
+ var p = window.parent.REAPER_INITIAL_SAVE;
2659
+ var parsed = typeof p === 'string' ? JSON.parse(p) : p;
2660
+ if (parsed && typeof parsed === 'object') Object.assign(memoryStore, parsed);
2661
+ }
2662
+ } catch(e) {}
2663
+
2664
+ if (Object.keys(memoryStore).length === 0) {
2665
+ try {
2666
+ if (window.parent && window.parent.localStorage) {
2667
+ var raw = window.parent.localStorage.getItem(STORAGE_KEY);
2668
+ if (raw) Object.assign(memoryStore, JSON.parse(raw));
2669
+ }
2670
+ } catch(e) {}
2671
+ }
2672
+
2673
+ if (Object.keys(memoryStore).length === 0) {
2674
+ try {
2675
+ if (window.opener && window.opener.localStorage) {
2676
+ var rawOpener = window.opener.localStorage.getItem(STORAGE_KEY);
2677
+ if (rawOpener) Object.assign(memoryStore, JSON.parse(rawOpener));
2678
+ }
2679
+ } catch(e) {}
2680
+ }
2681
+
2682
+ function persistSave() {
2683
+ var dataStr = JSON.stringify(memoryStore);
2684
+ try {
2685
+ if (window.parent && window.parent.localStorage) {
2686
+ window.parent.localStorage.setItem(STORAGE_KEY, dataStr);
2687
+ }
2688
+ } catch(e) {}
2689
+ try {
2690
+ if (window.opener && window.opener.localStorage) {
2691
+ window.opener.localStorage.setItem(STORAGE_KEY, dataStr);
2692
+ }
2693
+ } catch(e) {}
2694
+ try {
2695
+ var payload = { type: 'REAPER_SAVE_SYNC', gameId: GAME_ID, data: memoryStore };
2696
+ if (window.parent && window.parent !== window) {
2697
+ window.parent.postMessage(payload, '*');
2698
+ }
2699
+ if (window.opener) {
2700
+ window.opener.postMessage(payload, '*');
2701
+ }
2702
+ } catch(e) {}
2703
+ }
2704
+
2705
+ // Virtual Storage Implementation
2706
+ var virtualStorage = {
2707
+ getItem: function(k) {
2708
+ var key = String(k);
2709
+ return memoryStore.hasOwnProperty(key) ? memoryStore[key] : null;
2710
+ },
2711
+ setItem: function(k, v) {
2712
+ var key = String(k);
2713
+ var val = String(v);
2714
+ memoryStore[key] = val;
2715
+ persistSave();
2716
+ },
2717
+ removeItem: function(k) {
2718
+ var key = String(k);
2719
+ delete memoryStore[key];
2720
+ persistSave();
2721
+ },
2722
+ clear: function() {
2723
+ memoryStore = {};
2724
+ persistSave();
2725
+ },
2726
+ key: function(i) {
2727
+ var keys = Object.keys(memoryStore);
2728
+ return keys[i] !== undefined ? keys[i] : null;
2729
+ },
2730
+ get length() {
2731
+ return Object.keys(memoryStore).length;
2732
+ }
2733
+ };
2734
+
2735
+ // Proxy Handler for property access like storage.score = 50 or storage['save']
2736
+ var storageProxy = new Proxy(virtualStorage, {
2737
+ get: function(target, prop) {
2738
+ if (prop in target || typeof prop === 'symbol') {
2739
+ return target[prop];
2740
+ }
2741
+ return target.getItem(prop);
2742
+ },
2743
+ set: function(target, prop, val) {
2744
+ if (prop in target) {
2745
+ target[prop] = val;
2746
+ return true;
2747
+ }
2748
+ target.setItem(prop, val);
2749
+ return true;
2750
+ },
2751
+ deleteProperty: function(target, prop) {
2752
+ target.removeItem(prop);
2753
+ return true;
2754
+ }
2755
+ });
2756
+
2757
+ // Override localStorage & sessionStorage
2758
+ try {
2759
+ Object.defineProperty(window, 'localStorage', {
2760
+ value: storageProxy,
2761
+ configurable: true,
2762
+ enumerable: true,
2763
+ writable: true
2764
+ });
2765
+ } catch(e) {
2766
+ try { window.localStorage = storageProxy; } catch(e2) {}
2767
+ }
2768
+
2769
+ try {
2770
+ Object.defineProperty(window, 'sessionStorage', {
2771
+ value: storageProxy,
2772
+ configurable: true,
2773
+ enumerable: true,
2774
+ writable: true
2775
+ });
2776
+ } catch(e) {
2777
+ try { window.sessionStorage = storageProxy; } catch(e2) {}
2778
+ }
2779
+
2780
+ // Periodic & unload autosave sync
2781
+ setInterval(persistSave, 1500);
2782
+ window.addEventListener('beforeunload', persistSave);
2783
+ window.addEventListener('pagehide', persistSave);
2784
+
2785
+ // Focus game canvas on load & block intrusive popups
2786
+ window.open = function() { return null; };
2787
+ window.addEventListener('load', function() {
2788
+ var c = document.querySelector('canvas') || document.querySelector('#game');
2789
+ if (c && c.focus) c.focus();
2790
+ });
2791
+ })();
2642
2792
  <\/script>
2643
2793
  `;
2644
2794
 
2645
2795
  if (cleaned.includes('<head>')) {
2646
- cleaned = cleaned.replace('<head>', '<head>' + injectStyles);
2796
+ cleaned = cleaned.replace('<head>', '<head>' + injectStylesAndSave);
2647
2797
  } else if (cleaned.includes('<html>')) {
2648
- cleaned = cleaned.replace('<html>', '<html><head>' + injectStyles + '</head>');
2798
+ cleaned = cleaned.replace('<html>', '<html><head>' + injectStylesAndSave + '</head>');
2649
2799
  } else {
2650
- cleaned = injectStyles + cleaned;
2800
+ cleaned = injectStylesAndSave + cleaned;
2651
2801
  }
2652
2802
 
2653
2803
  return cleaned;
@@ -2704,7 +2854,7 @@ Query: **"${prompt}"**
2704
2854
  iframe.removeAttribute('srcdoc');
2705
2855
 
2706
2856
  if (this.turboGameCache && this.turboGameCache[filename]) {
2707
- iframe.srcdoc = this.cleanAndOptimizeGameHTML(this.turboGameCache[filename]);
2857
+ iframe.srcdoc = this.cleanAndOptimizeGameHTML(this.turboGameCache[filename], game.id);
2708
2858
  if (statusBadge) statusBadge.textContent = '⚡ TURBO INSTANT (60 FPS)';
2709
2859
  return;
2710
2860
  }
@@ -2717,7 +2867,7 @@ Query: **"${prompt}"**
2717
2867
  .then(html => {
2718
2868
  if (html && html.length > 50) {
2719
2869
  this.turboGameCache[filename] = html;
2720
- iframe.srcdoc = this.cleanAndOptimizeGameHTML(html);
2870
+ iframe.srcdoc = this.cleanAndOptimizeGameHTML(html, game.id);
2721
2871
  if (statusBadge) statusBadge.textContent = 'ONLINE (60 FPS)';
2722
2872
  } else {
2723
2873
  iframe.src = `https://gn-math.github.io/html/${filename}`;
@@ -2778,6 +2928,9 @@ Query: **"${prompt}"**
2778
2928
  const directUrl = `https://gn-math.github.io/html/${filename}`;
2779
2929
  const fallbackUrl = `https://fastly.jsdelivr.net/gh/gn-math/html@main/${filename}`;
2780
2930
 
2931
+ // Read stored game save from Reaper Hub local storage
2932
+ const initialSave = localStorage.getItem('reaper_save_' + game.id) || '{}';
2933
+
2781
2934
  // 1. Immediately open window in user click call stack to bypass browser popup blockers
2782
2935
  let win = null;
2783
2936
  try {
@@ -2864,8 +3017,20 @@ Query: **"${prompt}"**
2864
3017
  }
2865
3018
  @keyframes sp { to { transform: rotate(360deg); } }
2866
3019
  </style>
2867
- <script>
2868
- // Ad-block & Pop-up Neutralizer
3020
+ <script id="reaper-parent-bridge">
3021
+ // Local Save Data Hub Bridge & Relay
3022
+ window.REAPER_GAME_ID = "` + game.id + `";
3023
+ window.REAPER_INITIAL_SAVE = ` + JSON.stringify(initialSave) + `;
3024
+ window.addEventListener('message', function(e) {
3025
+ if (e.data && e.data.type === 'REAPER_SAVE_SYNC') {
3026
+ try {
3027
+ localStorage.setItem('reaper_save_' + e.data.gameId, JSON.stringify(e.data.data));
3028
+ } catch(err) {}
3029
+ try {
3030
+ if (window.opener) window.opener.postMessage(e.data, '*');
3031
+ } catch(err) {}
3032
+ }
3033
+ });
2869
3034
  window.open = function() { return null; };
2870
3035
  <\/script>
2871
3036
  </head>
@@ -2902,7 +3067,7 @@ Query: **"${prompt}"**
2902
3067
 
2903
3068
  // 3. High-Speed Memory Pre-Cache Check
2904
3069
  if (this.turboGameCache && this.turboGameCache[filename]) {
2905
- frame.srcdoc = this.cleanAndOptimizeGameHTML(this.turboGameCache[filename]);
3070
+ frame.srcdoc = this.cleanAndOptimizeGameHTML(this.turboGameCache[filename], game.id);
2906
3071
  frame.onload = finalizeLoad;
2907
3072
  setTimeout(finalizeLoad, 400);
2908
3073
  } else {
@@ -2915,7 +3080,7 @@ Query: **"${prompt}"**
2915
3080
  .then(html => {
2916
3081
  if (html && html.length > 50) {
2917
3082
  if (this.turboGameCache) this.turboGameCache[filename] = html;
2918
- frame.srcdoc = this.cleanAndOptimizeGameHTML(html);
3083
+ frame.srcdoc = this.cleanAndOptimizeGameHTML(html, game.id);
2919
3084
  frame.onload = finalizeLoad;
2920
3085
  setTimeout(finalizeLoad, 500);
2921
3086
  } else {
package/main/index.html CHANGED
@@ -38,7 +38,7 @@
38
38
  <div class="loader-brand-title">reaper</div>
39
39
  <div class="loader-badge-tag">
40
40
  <span class="badge-dot-live"></span>
41
- <span>V2.9.1 ZERO-LAG ENGINE</span> • 788 GAMES • 60 FPS
41
+ <span>V2.9.2 ZERO-LAG ENGINE</span> • 788 GAMES • LOCAL SAVE ENABLED
42
42
  </div>
43
43
 
44
44
  <!-- Live Terminal Kernel Diagnostics -->
package/main/js/app.js CHANGED
@@ -1,6 +1,6 @@
1
- // REAPER HUB - Core Engine (v2.9.1 100% Zero-Lag 60 FPS Engine & Auto about:blank Isolation)
2
- window.REAPER_VERSION = "2.9.1";
3
- window.REAPER_LATEST_URL = "https://unpkg.com/reaper-arcade-hub@2.9.1/main/index.html";
1
+ // REAPER HUB - Core Engine (v2.9.2 Persistent Local Save Engine & 60 FPS about:blank Isolation)
2
+ window.REAPER_VERSION = "2.9.2";
3
+ window.REAPER_LATEST_URL = "https://unpkg.com/reaper-arcade-hub@2.9.2/main/index.html";
4
4
 
5
5
  function _isMasterOwnerToken(k) {
6
6
  if (!k) return false;
@@ -283,7 +283,19 @@ class ReaperHubApp {
283
283
  }
284
284
  }
285
285
 
286
- // 11. Auto-show Update Logs on visit if new version
286
+ // 11. Realtime Persistent Game Save Synchronizer for about:blank tabs
287
+ window.addEventListener('message', (e) => {
288
+ if (e.data && e.data.type === 'REAPER_SAVE_SYNC' && e.data.gameId) {
289
+ try {
290
+ const key = 'reaper_save_' + e.data.gameId;
291
+ const current = JSON.parse(localStorage.getItem(key) || '{}');
292
+ Object.assign(current, e.data.data);
293
+ localStorage.setItem(key, JSON.stringify(current));
294
+ } catch(err) {}
295
+ }
296
+ });
297
+
298
+ // 12. Auto-show Update Logs on visit if new version
287
299
  const lastSeenVersion = localStorage.getItem('reaper_last_seen_version');
288
300
  if (lastSeenVersion !== '1.0.9') {
289
301
  setTimeout(() => {
@@ -2588,7 +2600,7 @@ Query: **"${prompt}"**
2588
2600
  }
2589
2601
  }
2590
2602
 
2591
- cleanAndOptimizeGameHTML(html) {
2603
+ cleanAndOptimizeGameHTML(html, gameId) {
2592
2604
  if (!html || typeof html !== 'string') return '';
2593
2605
  let cleaned = html;
2594
2606
 
@@ -2597,8 +2609,10 @@ Query: **"${prompt}"**
2597
2609
  cleaned = cleaned.replace(/<div id="sidebarad2"[\s\S]*?<\/div>\s*<\/div>/gi, '');
2598
2610
  cleaned = cleaned.replace(/<style>[\s\S]*?#sidebarad1[\s\S]*?<\/style>/gi, '');
2599
2611
 
2600
- // 2. Inject high-performance responsive scaling, centering & click-focus engine
2601
- const injectStyles = `
2612
+ const safeGameId = String(gameId || (this.currentGame ? this.currentGame.id : 'default'));
2613
+
2614
+ // 2. Inject high-performance responsive scaling & Universal Persistent Local Save Engine
2615
+ const injectStylesAndSave = `
2602
2616
  <style id="reaper-game-enhancer">
2603
2617
  * { box-sizing: border-box; }
2604
2618
  html, body {
@@ -2632,22 +2646,158 @@ Query: **"${prompt}"**
2632
2646
  z-index: -9999 !important;
2633
2647
  }
2634
2648
  </style>
2635
- <script>
2636
- // Neutralize ad popups & focus game canvas
2637
- window.open = function() { return null; };
2638
- window.addEventListener('load', function() {
2639
- const c = document.querySelector('canvas') || document.querySelector('#game');
2640
- if (c && c.focus) c.focus();
2641
- });
2649
+ <script id="reaper-save-engine">
2650
+ (function() {
2651
+ var GAME_ID = "` + safeGameId + `";
2652
+ var STORAGE_KEY = "reaper_save_" + GAME_ID;
2653
+ var memoryStore = {};
2654
+
2655
+ // Initial State Restoration from Parent Window or Opener
2656
+ try {
2657
+ if (window.parent && window.parent.REAPER_INITIAL_SAVE) {
2658
+ var p = window.parent.REAPER_INITIAL_SAVE;
2659
+ var parsed = typeof p === 'string' ? JSON.parse(p) : p;
2660
+ if (parsed && typeof parsed === 'object') Object.assign(memoryStore, parsed);
2661
+ }
2662
+ } catch(e) {}
2663
+
2664
+ if (Object.keys(memoryStore).length === 0) {
2665
+ try {
2666
+ if (window.parent && window.parent.localStorage) {
2667
+ var raw = window.parent.localStorage.getItem(STORAGE_KEY);
2668
+ if (raw) Object.assign(memoryStore, JSON.parse(raw));
2669
+ }
2670
+ } catch(e) {}
2671
+ }
2672
+
2673
+ if (Object.keys(memoryStore).length === 0) {
2674
+ try {
2675
+ if (window.opener && window.opener.localStorage) {
2676
+ var rawOpener = window.opener.localStorage.getItem(STORAGE_KEY);
2677
+ if (rawOpener) Object.assign(memoryStore, JSON.parse(rawOpener));
2678
+ }
2679
+ } catch(e) {}
2680
+ }
2681
+
2682
+ function persistSave() {
2683
+ var dataStr = JSON.stringify(memoryStore);
2684
+ try {
2685
+ if (window.parent && window.parent.localStorage) {
2686
+ window.parent.localStorage.setItem(STORAGE_KEY, dataStr);
2687
+ }
2688
+ } catch(e) {}
2689
+ try {
2690
+ if (window.opener && window.opener.localStorage) {
2691
+ window.opener.localStorage.setItem(STORAGE_KEY, dataStr);
2692
+ }
2693
+ } catch(e) {}
2694
+ try {
2695
+ var payload = { type: 'REAPER_SAVE_SYNC', gameId: GAME_ID, data: memoryStore };
2696
+ if (window.parent && window.parent !== window) {
2697
+ window.parent.postMessage(payload, '*');
2698
+ }
2699
+ if (window.opener) {
2700
+ window.opener.postMessage(payload, '*');
2701
+ }
2702
+ } catch(e) {}
2703
+ }
2704
+
2705
+ // Virtual Storage Implementation
2706
+ var virtualStorage = {
2707
+ getItem: function(k) {
2708
+ var key = String(k);
2709
+ return memoryStore.hasOwnProperty(key) ? memoryStore[key] : null;
2710
+ },
2711
+ setItem: function(k, v) {
2712
+ var key = String(k);
2713
+ var val = String(v);
2714
+ memoryStore[key] = val;
2715
+ persistSave();
2716
+ },
2717
+ removeItem: function(k) {
2718
+ var key = String(k);
2719
+ delete memoryStore[key];
2720
+ persistSave();
2721
+ },
2722
+ clear: function() {
2723
+ memoryStore = {};
2724
+ persistSave();
2725
+ },
2726
+ key: function(i) {
2727
+ var keys = Object.keys(memoryStore);
2728
+ return keys[i] !== undefined ? keys[i] : null;
2729
+ },
2730
+ get length() {
2731
+ return Object.keys(memoryStore).length;
2732
+ }
2733
+ };
2734
+
2735
+ // Proxy Handler for property access like storage.score = 50 or storage['save']
2736
+ var storageProxy = new Proxy(virtualStorage, {
2737
+ get: function(target, prop) {
2738
+ if (prop in target || typeof prop === 'symbol') {
2739
+ return target[prop];
2740
+ }
2741
+ return target.getItem(prop);
2742
+ },
2743
+ set: function(target, prop, val) {
2744
+ if (prop in target) {
2745
+ target[prop] = val;
2746
+ return true;
2747
+ }
2748
+ target.setItem(prop, val);
2749
+ return true;
2750
+ },
2751
+ deleteProperty: function(target, prop) {
2752
+ target.removeItem(prop);
2753
+ return true;
2754
+ }
2755
+ });
2756
+
2757
+ // Override localStorage & sessionStorage
2758
+ try {
2759
+ Object.defineProperty(window, 'localStorage', {
2760
+ value: storageProxy,
2761
+ configurable: true,
2762
+ enumerable: true,
2763
+ writable: true
2764
+ });
2765
+ } catch(e) {
2766
+ try { window.localStorage = storageProxy; } catch(e2) {}
2767
+ }
2768
+
2769
+ try {
2770
+ Object.defineProperty(window, 'sessionStorage', {
2771
+ value: storageProxy,
2772
+ configurable: true,
2773
+ enumerable: true,
2774
+ writable: true
2775
+ });
2776
+ } catch(e) {
2777
+ try { window.sessionStorage = storageProxy; } catch(e2) {}
2778
+ }
2779
+
2780
+ // Periodic & unload autosave sync
2781
+ setInterval(persistSave, 1500);
2782
+ window.addEventListener('beforeunload', persistSave);
2783
+ window.addEventListener('pagehide', persistSave);
2784
+
2785
+ // Focus game canvas on load & block intrusive popups
2786
+ window.open = function() { return null; };
2787
+ window.addEventListener('load', function() {
2788
+ var c = document.querySelector('canvas') || document.querySelector('#game');
2789
+ if (c && c.focus) c.focus();
2790
+ });
2791
+ })();
2642
2792
  <\/script>
2643
2793
  `;
2644
2794
 
2645
2795
  if (cleaned.includes('<head>')) {
2646
- cleaned = cleaned.replace('<head>', '<head>' + injectStyles);
2796
+ cleaned = cleaned.replace('<head>', '<head>' + injectStylesAndSave);
2647
2797
  } else if (cleaned.includes('<html>')) {
2648
- cleaned = cleaned.replace('<html>', '<html><head>' + injectStyles + '</head>');
2798
+ cleaned = cleaned.replace('<html>', '<html><head>' + injectStylesAndSave + '</head>');
2649
2799
  } else {
2650
- cleaned = injectStyles + cleaned;
2800
+ cleaned = injectStylesAndSave + cleaned;
2651
2801
  }
2652
2802
 
2653
2803
  return cleaned;
@@ -2704,7 +2854,7 @@ Query: **"${prompt}"**
2704
2854
  iframe.removeAttribute('srcdoc');
2705
2855
 
2706
2856
  if (this.turboGameCache && this.turboGameCache[filename]) {
2707
- iframe.srcdoc = this.cleanAndOptimizeGameHTML(this.turboGameCache[filename]);
2857
+ iframe.srcdoc = this.cleanAndOptimizeGameHTML(this.turboGameCache[filename], game.id);
2708
2858
  if (statusBadge) statusBadge.textContent = '⚡ TURBO INSTANT (60 FPS)';
2709
2859
  return;
2710
2860
  }
@@ -2717,7 +2867,7 @@ Query: **"${prompt}"**
2717
2867
  .then(html => {
2718
2868
  if (html && html.length > 50) {
2719
2869
  this.turboGameCache[filename] = html;
2720
- iframe.srcdoc = this.cleanAndOptimizeGameHTML(html);
2870
+ iframe.srcdoc = this.cleanAndOptimizeGameHTML(html, game.id);
2721
2871
  if (statusBadge) statusBadge.textContent = 'ONLINE (60 FPS)';
2722
2872
  } else {
2723
2873
  iframe.src = `https://gn-math.github.io/html/${filename}`;
@@ -2778,6 +2928,9 @@ Query: **"${prompt}"**
2778
2928
  const directUrl = `https://gn-math.github.io/html/${filename}`;
2779
2929
  const fallbackUrl = `https://fastly.jsdelivr.net/gh/gn-math/html@main/${filename}`;
2780
2930
 
2931
+ // Read stored game save from Reaper Hub local storage
2932
+ const initialSave = localStorage.getItem('reaper_save_' + game.id) || '{}';
2933
+
2781
2934
  // 1. Immediately open window in user click call stack to bypass browser popup blockers
2782
2935
  let win = null;
2783
2936
  try {
@@ -2864,8 +3017,20 @@ Query: **"${prompt}"**
2864
3017
  }
2865
3018
  @keyframes sp { to { transform: rotate(360deg); } }
2866
3019
  </style>
2867
- <script>
2868
- // Ad-block & Pop-up Neutralizer
3020
+ <script id="reaper-parent-bridge">
3021
+ // Local Save Data Hub Bridge & Relay
3022
+ window.REAPER_GAME_ID = "` + game.id + `";
3023
+ window.REAPER_INITIAL_SAVE = ` + JSON.stringify(initialSave) + `;
3024
+ window.addEventListener('message', function(e) {
3025
+ if (e.data && e.data.type === 'REAPER_SAVE_SYNC') {
3026
+ try {
3027
+ localStorage.setItem('reaper_save_' + e.data.gameId, JSON.stringify(e.data.data));
3028
+ } catch(err) {}
3029
+ try {
3030
+ if (window.opener) window.opener.postMessage(e.data, '*');
3031
+ } catch(err) {}
3032
+ }
3033
+ });
2869
3034
  window.open = function() { return null; };
2870
3035
  <\/script>
2871
3036
  </head>
@@ -2902,7 +3067,7 @@ Query: **"${prompt}"**
2902
3067
 
2903
3068
  // 3. High-Speed Memory Pre-Cache Check
2904
3069
  if (this.turboGameCache && this.turboGameCache[filename]) {
2905
- frame.srcdoc = this.cleanAndOptimizeGameHTML(this.turboGameCache[filename]);
3070
+ frame.srcdoc = this.cleanAndOptimizeGameHTML(this.turboGameCache[filename], game.id);
2906
3071
  frame.onload = finalizeLoad;
2907
3072
  setTimeout(finalizeLoad, 400);
2908
3073
  } else {
@@ -2915,7 +3080,7 @@ Query: **"${prompt}"**
2915
3080
  .then(html => {
2916
3081
  if (html && html.length > 50) {
2917
3082
  if (this.turboGameCache) this.turboGameCache[filename] = html;
2918
- frame.srcdoc = this.cleanAndOptimizeGameHTML(html);
3083
+ frame.srcdoc = this.cleanAndOptimizeGameHTML(html, game.id);
2919
3084
  frame.onload = finalizeLoad;
2920
3085
  setTimeout(finalizeLoad, 500);
2921
3086
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reaper-arcade-hub",
3
- "version": "2.9.1",
3
+ "version": "2.9.2",
4
4
  "description": "Private Core Hub",
5
5
  "main": "main/index.html",
6
6
  "unpkg": "main/index.html",