Changeset 3365629
- Timestamp:
- 09/22/2025 08:23:48 AM (7 months ago)
- Location:
- ready-made-oxygen-integration
- Files:
-
- 14 added
- 6 edited
-
tags/1.2.0 (added)
-
tags/1.2.0/README.md (added)
-
tags/1.2.0/includes (added)
-
tags/1.2.0/includes/js (added)
-
tags/1.2.0/includes/js/addPasteButton-modular.js (added)
-
tags/1.2.0/includes/js/modules (added)
-
tags/1.2.0/includes/js/modules/fontManager.js (added)
-
tags/1.2.0/includes/js/modules/fontProcessor.js (added)
-
tags/1.2.0/includes/js/modules/imageProcessor.js (added)
-
tags/1.2.0/includes/js/modules/pasteHandler.js (added)
-
tags/1.2.0/includes/js/modules/toastNotifications.js (added)
-
tags/1.2.0/includes/js/modules/uiComponents.js (added)
-
tags/1.2.0/readme.txt (added)
-
tags/1.2.0/ready-made-oxygen-integration.php (added)
-
trunk/README.md (modified) (3 diffs)
-
trunk/includes/js/addPasteButton-modular.js (modified) (1 diff)
-
trunk/includes/js/modules/pasteHandler.js (modified) (2 diffs)
-
trunk/includes/js/modules/uiComponents.js (modified) (3 diffs)
-
trunk/readme.txt (modified) (1 diff)
-
trunk/ready-made-oxygen-integration.php (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
ready-made-oxygen-integration/trunk/README.md
r3322581 r3365629 1 1 # Ready-Made Oxygen Integration 2 2 3 [](https://levels.dev)3 [](https://levels.dev) 4 4 [](https://wordpress.org) 5 5 [](https://oxygenbuilder.com) … … 81 81 ## 🛠️ Troubleshooting 82 82 83 If pasting doesn ’t work:83 If pasting doesn't work: 84 84 85 85 - Ensure plugin is active 86 86 - Clear browser cache 87 - Confirm you ’re using Oxygen 4+87 - Confirm you're using Oxygen 4+ 88 88 - Disable conflicting plugins temporarily 89 90 ### Firefox Users 91 - **First time**: A permission modal will appear - paste your JSON and click "Allow & Paste" 92 - **Subsequent times**: Direct pasting works without modal 93 - **If issues persist**: Refresh page to reset permissions 89 94 90 95 --- … … 112 117 ## 📓 Changelog 113 118 119 ### 1.2.0 – Firefox Compatibility 120 - ✨ **Firefox Support**: Complete Firefox clipboard compatibility with unified permission modal 121 - 🛡️ **Context Menu Fix**: Eliminated unwanted "Einfügen" context menu in Firefox 122 - 🔧 **Browser Detection**: Chrome users get direct clipboard access, Firefox users get specialized handling 123 - 📱 **Session Memory**: Firefox permission modal only appears once per session 124 - 🚀 **Improved UX**: Streamlined workflow - permission + paste in single action 125 - 🔄 **Error Recovery**: Automatic permission reset and retry on clipboard failures 126 114 127 ### 1.1.0 – Font Matching Implementation 115 128 - Font matching feature added -
ready-made-oxygen-integration/trunk/includes/js/addPasteButton-modular.js
r3322576 r3365629 3 3 * Modular structure for better maintainability 4 4 * 5 * Version: 1. 1.0 (Font Matching)6 */ 7 8 console.log("READOXIN: ✅ Plugin loaded v1. 1.0");5 * Version: 1.2.0 (Firefox Compatibility) 6 */ 7 8 console.log("READOXIN: ✅ Plugin loaded v1.2.0"); 9 9 10 10 // Verify modules are loaded -
ready-made-oxygen-integration/trunk/includes/js/modules/pasteHandler.js
r3356947 r3365629 4 4 */ 5 5 6 /** 7 * Enhanced paste logic with comprehensive logging 6 // Browser detection 7 const isFirefox = navigator.userAgent.toLowerCase().includes('firefox'); 8 9 // Session-based Firefox permission tracking 10 let firefoxPermissionGranted = false; 11 let modalCurrentlyOpen = false; 12 13 /** 14 * Clean Firefox-compatible clipboard reading with session awareness 15 * @returns {Promise<string>} Clipboard text content 16 */ 17 async function readClipboardText() { 18 // For Chrome and other browsers, try clipboard API directly 19 if (!isFirefox) { 20 if (navigator.clipboard && navigator.clipboard.readText) { 21 try { 22 const text = await navigator.clipboard.readText(); 23 return text; 24 } catch (error) { 25 console.warn("READOXIN: ❌ Clipboard access failed:", error.message); 26 throw error; 27 } 28 } else { 29 throw new Error('Clipboard API not available in this browser'); 30 } 31 } 32 33 // Firefox-specific handling below 34 // If we have session permission, try clipboard directly 35 if (firefoxPermissionGranted) { 36 if (navigator.clipboard && navigator.clipboard.readText) { 37 try { 38 const text = await navigator.clipboard.readText(); 39 return text; 40 } catch (error) { 41 // Reset permission and fall through to modal 42 firefoxPermissionGranted = false; 43 } 44 } 45 } 46 47 // No permission or clipboard failed - show unified modal (if not already open) 48 if (modalCurrentlyOpen) { 49 throw new Error('Modal already open'); 50 } 51 52 const text = await showUnifiedFirefoxModal(); 53 54 // If we got text from the modal, mark permission as granted 55 if (text) { 56 firefoxPermissionGranted = true; 57 } 58 59 return text; 60 } 61 62 /** 63 * Unified Firefox modal - combines permission request with paste functionality 64 * @returns {Promise<string>} Clipboard text content from user 65 */ 66 function showUnifiedFirefoxModal() { 67 return new Promise((resolve, reject) => { 68 // Set modal flag 69 modalCurrentlyOpen = true; 70 71 // Create modal overlay 72 const overlay = document.createElement('div'); 73 overlay.style.cssText = ` 74 position: fixed; 75 top: 0; 76 left: 0; 77 width: 100%; 78 height: 100%; 79 background: rgba(0, 0, 0, 0.7); 80 z-index: 999999; 81 display: flex; 82 align-items: center; 83 justify-content: center; 84 `; 85 86 // Create modal content 87 const modal = document.createElement('div'); 88 modal.style.cssText = ` 89 background: white; 90 border-radius: 12px; 91 padding: 24px; 92 max-width: 500px; 93 width: 90%; 94 box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); 95 `; 96 97 modal.innerHTML = ` 98 <div style="display: flex; align-items: center; gap: 12px; margin-bottom: 16px;"> 99 <div style="font-size: 32px;">🦊</div> 100 <h3 style="margin: 0; color: #333; font-family: system-ui, -apple-system, sans-serif; font-size: 18px;"> 101 Firefox Paste Permission 102 </h3> 103 </div> 104 <p style="margin: 0 0 16px 0; color: #666; font-family: system-ui, -apple-system, sans-serif; line-height: 1.4; font-size: 14px;"> 105 Paste your Figma JSON below to grant clipboard permission and paste automatically: 106 </p> 107 <textarea id="unifiedPasteInput" placeholder="Paste your Figma JSON here (Ctrl+V)" style=" 108 width: 100%; 109 height: 150px; 110 border: 2px solid #ddd; 111 border-radius: 6px; 112 padding: 12px; 113 font-family: 'Courier New', monospace; 114 font-size: 12px; 115 resize: vertical; 116 box-sizing: border-box; 117 outline: none; 118 background: #fafafa; 119 "></textarea> 120 <div style="display: flex; gap: 12px; justify-content: flex-end; margin-top: 16px;"> 121 <button id="unifiedCancel" style=" 122 padding: 8px 16px; 123 border: 2px solid #ddd; 124 background: white; 125 border-radius: 6px; 126 cursor: pointer; 127 font-family: system-ui, -apple-system, sans-serif; 128 color: #666; 129 ">Cancel</button> 130 <button id="unifiedAllowPaste" style=" 131 padding: 8px 16px; 132 border: 2px solid #9580FF; 133 background: #9580FF; 134 color: white; 135 border-radius: 6px; 136 cursor: pointer; 137 font-family: system-ui, -apple-system, sans-serif; 138 font-weight: 500; 139 ">Allow & Paste</button> 140 </div> 141 `; 142 143 overlay.appendChild(modal); 144 document.body.appendChild(overlay); 145 146 // Focus textarea 147 const textarea = modal.querySelector('#unifiedPasteInput'); 148 textarea.focus(); 149 150 // Handle Allow & Paste button 151 modal.querySelector('#unifiedAllowPaste').addEventListener('click', async () => { 152 const text = textarea.value.trim(); 153 154 // Clean up modal 155 document.body.removeChild(overlay); 156 modalCurrentlyOpen = false; 157 158 if (text) { 159 // Try to grant clipboard permission 160 try { 161 if (navigator.clipboard && navigator.clipboard.readText) { 162 await navigator.clipboard.readText(); 163 } 164 } catch (error) { 165 // Permission attempt completed 166 } 167 168 resolve(text); 169 } else { 170 reject(new Error('No text provided')); 171 } 172 }); 173 174 // Handle cancel button 175 modal.querySelector('#unifiedCancel').addEventListener('click', () => { 176 document.body.removeChild(overlay); 177 modalCurrentlyOpen = false; 178 reject(new Error('User cancelled')); 179 }); 180 181 // Auto-submit on Ctrl+V 182 textarea.addEventListener('keydown', (e) => { 183 if (e.ctrlKey && e.key === 'v') { 184 setTimeout(() => { 185 if (textarea.value.trim()) { 186 modal.querySelector('#unifiedAllowPaste').click(); 187 } 188 }, 100); 189 } 190 }); 191 192 // Handle escape key 193 document.addEventListener('keydown', function escapeHandler(e) { 194 if (e.key === 'Escape') { 195 document.removeEventListener('keydown', escapeHandler); 196 if (document.body.contains(overlay)) { 197 document.body.removeChild(overlay); 198 modalCurrentlyOpen = false; 199 reject(new Error('User cancelled')); 200 } 201 } 202 }); 203 }); 204 } 205 206 207 /** 208 * Enhanced paste logic with comprehensive logging and Firefox compatibility 8 209 * @param {Object} scope - Angular scope from Oxygen builder 9 210 */ … … 14 215 } 15 216 16 navigator.clipboard.readText().then(async (clipboardText) => {217 readClipboardText().then(async (clipboardText) => { 17 218 try { 18 219 let json = JSON.parse(clipboardText); -
ready-made-oxygen-integration/trunk/includes/js/modules/uiComponents.js
r3322576 r3365629 43 43 border: "2px solid #8d8bd9", 44 44 }); 45 button.addEventListener("click", onClick); 45 // Firefox-specific: Disable all default behaviors that could interfere 46 button.addEventListener("mousedown", (e) => { 47 e.preventDefault(); 48 e.stopPropagation(); 49 }); 50 51 button.addEventListener("mouseup", (e) => { 52 e.preventDefault(); 53 e.stopPropagation(); 54 }); 55 56 button.addEventListener("click", (e) => { 57 e.preventDefault(); 58 e.stopPropagation(); 59 e.stopImmediatePropagation(); 60 61 // Execute immediately without waiting for browser events 62 setTimeout(() => onClick(), 0); 63 }); 64 65 // Completely disable context menu on this button 66 button.addEventListener("contextmenu", (e) => { 67 e.preventDefault(); 68 e.stopPropagation(); 69 e.stopImmediatePropagation(); 70 return false; 71 }); 72 73 // Disable Firefox's paste-related shortcuts on this button 74 button.addEventListener("keydown", (e) => { 75 if (e.ctrlKey && e.key === 'v') { 76 e.preventDefault(); 77 e.stopPropagation(); 78 e.stopImmediatePropagation(); 79 } 80 }); 81 82 // Override any tooltip attributes 83 button.removeAttribute("data-tooltip"); 84 button.removeAttribute("aria-label"); 85 86 // Add Firefox-specific attributes to disable context menu behavior 87 button.setAttribute('oncontextmenu', 'return false;'); 88 button.setAttribute('onselectstart', 'return false;'); 89 button.setAttribute('ondragstart', 'return false;'); 90 46 91 // document.body.appendChild(button); 47 92 toolbar.parentNode.insertBefore(button, toolbar.nextSibling); 93 94 // Global Firefox context menu suppression near our button 95 suppressFirefoxContextMenu(button); 48 96 49 97 const style = document.createElement("style"); … … 59 107 60 108 /** 61 * Set up keyboard shortcuts for paste functionality 109 * Suppress Firefox context menu behavior around our paste button 110 * @param {HTMLElement} button - The paste button element 111 */ 112 function suppressFirefoxContextMenu(button) { 113 114 // Create invisible overlay to capture context menu events 115 const overlay = document.createElement('div'); 116 overlay.style.cssText = ` 117 position: absolute; 118 pointer-events: none; 119 z-index: 9998; 120 `; 121 122 // Position overlay over button 123 function updateOverlayPosition() { 124 const rect = button.getBoundingClientRect(); 125 overlay.style.left = rect.left - 10 + 'px'; 126 overlay.style.top = rect.top - 10 + 'px'; 127 overlay.style.width = rect.width + 20 + 'px'; 128 overlay.style.height = rect.height + 20 + 'px'; 129 overlay.style.pointerEvents = 'all'; 130 } 131 132 updateOverlayPosition(); 133 document.body.appendChild(overlay); 134 135 // Block all context menu events in this area 136 overlay.addEventListener('contextmenu', (e) => { 137 e.preventDefault(); 138 e.stopPropagation(); 139 e.stopImmediatePropagation(); 140 return false; 141 }); 142 143 // Update overlay position when window resizes or scrolls 144 window.addEventListener('resize', updateOverlayPosition); 145 window.addEventListener('scroll', updateOverlayPosition); 146 147 // Enhanced Firefox context menu suppression 148 let contextMenuBlocked = false; 149 let suppressionActive = false; 150 151 // Block context menu during any interaction with our button 152 function enableContextMenuSuppression() { 153 if (!suppressionActive) { 154 suppressionActive = true; 155 156 // Multiple event listeners for comprehensive blocking 157 document.addEventListener('contextmenu', blockContextMenu, {capture: true, passive: false}); 158 document.addEventListener('selectstart', blockContextMenu, {capture: true, passive: false}); 159 document.addEventListener('dragstart', blockContextMenu, {capture: true, passive: false}); 160 161 // Also block on window level (for Firefox edge cases) 162 window.addEventListener('contextmenu', blockContextMenu, {capture: true, passive: false}); 163 } 164 } 165 166 function disableContextMenuSuppression() { 167 if (suppressionActive) { 168 suppressionActive = false; 169 170 // Longer delay to prevent context menu after modal interactions 171 setTimeout(() => { 172 if (!suppressionActive) { // Only disable if not re-activated 173 document.removeEventListener('contextmenu', blockContextMenu, {capture: true}); 174 document.removeEventListener('selectstart', blockContextMenu, {capture: true}); 175 document.removeEventListener('dragstart', blockContextMenu, {capture: true}); 176 window.removeEventListener('contextmenu', blockContextMenu, {capture: true}); 177 } 178 }, 500); // Longer delay for modal interactions 179 } 180 } 181 182 function blockContextMenu(e) { 183 e.preventDefault(); 184 e.stopPropagation(); 185 e.stopImmediatePropagation(); 186 return false; 187 } 188 189 // Activate suppression on any interaction 190 button.addEventListener('mouseenter', enableContextMenuSuppression); 191 button.addEventListener('mousedown', enableContextMenuSuppression); 192 button.addEventListener('focus', enableContextMenuSuppression); 193 194 // Deactivate suppression when leaving 195 button.addEventListener('mouseleave', disableContextMenuSuppression); 196 button.addEventListener('blur', disableContextMenuSuppression); 197 } 198 199 /** 200 * Set up keyboard shortcuts for paste functionality with Firefox support 62 201 * @param {Function} pasteHandler - Function to call when shortcut is triggered 63 202 */ … … 65 204 document.addEventListener("keydown", function (event) { 66 205 if (event.ctrlKey && event.altKey && event.key === "v") { 206 event.preventDefault(); 67 207 pasteHandler(); 68 208 } 69 209 }); 210 211 // Firefox-specific: Also listen for regular Ctrl+V when in Oxygen iframe 212 document.addEventListener("keydown", function (event) { 213 if (event.ctrlKey && event.key === "v" && !event.altKey) { 214 // Check if we're in the Oxygen builder context 215 const isInBuilder = document.querySelector('.oxygen-toolbar-menus') || 216 document.querySelector('[ng-controller="BuilderController"]'); 217 218 if (isInBuilder && event.target.tagName !== 'TEXTAREA' && event.target.tagName !== 'INPUT') { 219 event.preventDefault(); 220 pasteHandler(); 221 } 222 } 223 }); 70 224 } -
ready-made-oxygen-integration/trunk/readme.txt
r3356950 r3365629 71 71 == Changelog == 72 72 73 = 1.2.0 = 74 * Firefox support added 75 73 76 = 1.1.1 = 74 77 * Enhanced image processor and paste handler modules -
ready-made-oxygen-integration/trunk/ready-made-oxygen-integration.php
r3356950 r3365629 7 7 * Plugin URI: https://levels.dev/automatic-webdevelopment-from-figma-via-copy-paste 8 8 * Description: Essential integration for the Ready-Made Figma plugin to enable copy-paste functionality with Oxygen 4. 9 * Version: 1. 1.19 * Version: 1.2.0 10 10 * Author: levels.dev 11 11 * Author URI: https://levels.dev … … 22 22 define('SCRIPT_DEBUG', true); 23 23 // Define plugin version 24 define('READOXIN_JS_VERSION', '1. 1.1');24 define('READOXIN_JS_VERSION', '1.2.0'); 25 25 26 26 // Use global namespace for WordPress hooks
Note: See TracChangeset
for help on using the changeset viewer.