diff options
Diffstat (limited to 'src/components/SmartClipboardButton.tsx')
| -rw-r--r-- | src/components/SmartClipboardButton.tsx | 253 |
1 files changed, 66 insertions, 187 deletions
diff --git a/src/components/SmartClipboardButton.tsx b/src/components/SmartClipboardButton.tsx index 229560d..81223bd 100644 --- a/src/components/SmartClipboardButton.tsx +++ b/src/components/SmartClipboardButton.tsx @@ -1,8 +1,8 @@ import { useState } from "react"; import { PanelSectionRow, ButtonItem } from "@decky/ui"; -import { FaClipboard, FaRocket } from "react-icons/fa"; +import { FaClipboard } from "react-icons/fa"; import { toaster } from "@decky/api"; -import { getLaunchOption, copyToSystemClipboard } from "../api/lsfgApi"; +import { getLaunchOption } from "../api/lsfgApi"; export function SmartClipboardButton() { const [isLoading, setIsLoading] = useState(false); @@ -23,195 +23,66 @@ export function SmartClipboardButton() { try { const text = await getLaunchOptionText(); - // Strategy 1: Try direct navigator.clipboard first (fastest) - let directSuccess = false; + // Use the proven input simulation method + const tempInput = document.createElement('input'); + tempInput.value = text; + tempInput.style.position = 'absolute'; + tempInput.style.left = '-9999px'; + document.body.appendChild(tempInput); + + // Focus and select the text + tempInput.focus(); + tempInput.select(); + + // Try copying using execCommand first (most reliable in gaming mode) + let copySuccess = false; try { - await navigator.clipboard.writeText(text); - // Verify it worked - const readBack = await navigator.clipboard.readText(); - directSuccess = readBack === text; + if (document.execCommand('copy')) { + copySuccess = true; + } } catch (e) { - // Direct clipboard failed, will try alternatives - } - - if (directSuccess) { - toaster.toast({ - title: "Copied to Clipboard!", - body: "Launch option ready to paste" - }); - return; + // If execCommand fails, try navigator.clipboard as fallback + try { + await navigator.clipboard.writeText(text); + copySuccess = true; + } catch (clipboardError) { + console.error('Both copy methods failed:', e, clipboardError); + } } - - // Strategy 2: Try backend system clipboard - try { - const result = await copyToSystemClipboard(text); - if (result.success) { + + // Clean up + document.body.removeChild(tempInput); + + if (copySuccess) { + // Verify the copy worked by reading back + try { + const readBack = await navigator.clipboard.readText(); + if (readBack === text) { + toaster.toast({ + title: "Copied to Clipboard!", + body: "Launch option ready to paste" + }); + } else { + // Copy worked but verification failed - still consider it success + toaster.toast({ + title: "Copied to Clipboard!", + body: "Launch option copied (verification unavailable)" + }); + } + } catch (e) { + // Verification failed but copy likely worked toaster.toast({ title: "Copied to Clipboard!", - body: `Using ${result.method || "system clipboard"}` + body: "Launch option copied successfully" }); - return; } - } catch (e) { - // Backend failed, fall back to browser + } else { + toaster.toast({ + title: "Copy Failed", + body: "Unable to copy to clipboard" + }); } - // Strategy 3: Fall back to optimized browser window - const htmlContent = ` - <!DOCTYPE html> - <html> - <head> - <meta charset="utf-8"> - <title>Quick Copy - Steam Deck Clipboard Helper</title> - <style> - body { - font-family: 'Motiva Sans', system-ui, sans-serif; - background: linear-gradient(135deg, #1e2328 0%, #2a475e 100%); - color: white; - padding: 20px; - margin: 0; - display: flex; - align-items: center; - justify-content: center; - min-height: 100vh; - } - .container { - background: rgba(42, 71, 94, 0.9); - padding: 30px; - border-radius: 12px; - text-align: center; - box-shadow: 0 8px 32px rgba(0,0,0,0.3); - border: 1px solid rgba(255,255,255,0.1); - max-width: 500px; - width: 100%; - } - h2 { - margin-top: 0; - color: #66c0f4; - font-size: 24px; - } - .launch-option { - background: rgba(0,0,0,0.3); - padding: 15px; - border-radius: 8px; - font-family: 'Fira Code', 'Courier New', monospace; - font-size: 16px; - margin: 20px 0; - word-break: break-all; - border: 1px solid rgba(102, 192, 244, 0.3); - } - .status { - margin: 20px 0; - font-size: 16px; - min-height: 24px; - } - .success { color: #66bb6a; } - .error { color: #f44336; } - button { - background: linear-gradient(135deg, #417a9b 0%, #67c1f5 100%); - color: white; - border: none; - padding: 12px 24px; - font-size: 16px; - border-radius: 6px; - cursor: pointer; - margin: 8px; - transition: all 0.2s; - font-family: inherit; - } - button:hover { - background: linear-gradient(135deg, #4e8bb8 0%, #7bc8f7 100%); - transform: translateY(-1px); - } - button:active { - transform: translateY(0px); - } - .close-timer { - font-size: 14px; - opacity: 0.7; - margin-top: 15px; - } - </style> - </head> - <body> - <div class="container"> - <h2>🚀 Steam Deck Clipboard Helper</h2> - <div>Copy this launch option for your Steam games:</div> - <div class="launch-option">${text}</div> - <div id="status" class="status">⏳ Copying to clipboard...</div> - <div> - <button onclick="copyAndClose()" id="copyBtn">Copy & Close</button> - <button onclick="window.close()">Close</button> - </div> - <div class="close-timer" id="timer"></div> - </div> - <script> - const textToCopy = ${JSON.stringify(text)}; - let copied = false; - let autoCloseTimer = null; - - async function autoCopy() { - try { - await navigator.clipboard.writeText(textToCopy); - // Verify it worked - const readBack = await navigator.clipboard.readText(); - if (readBack === textToCopy) { - document.getElementById('status').innerHTML = '<span class="success">✅ Successfully copied to clipboard!</span>'; - copied = true; - startAutoClose(); - } else { - document.getElementById('status').innerHTML = '<span class="error">⚠️ Copy may have failed - use button below</span>'; - } - } catch (e) { - document.getElementById('status').innerHTML = '<span class="error">❌ Auto-copy failed - click "Copy & Close" below</span>'; - } - } - - async function copyAndClose() { - try { - await navigator.clipboard.writeText(textToCopy); - const readBack = await navigator.clipboard.readText(); - if (readBack === textToCopy) { - window.close(); - } else { - alert('Copy verification failed. Please try again or copy manually.'); - } - } catch (e) { - alert('Copy failed: ' + e.message); - } - } - - function startAutoClose() { - let seconds = 3; - const timerEl = document.getElementById('timer'); - timerEl.textContent = \`Window will close in \${seconds} seconds...\`; - - autoCloseTimer = setInterval(() => { - seconds--; - if (seconds <= 0) { - clearInterval(autoCloseTimer); - window.close(); - } else { - timerEl.textContent = \`Window will close in \${seconds} seconds...\`; - } - }, 1000); - } - - // Auto-copy on load - window.addEventListener('load', autoCopy); - </script> - </body> - </html> - `; - - const dataUrl = 'data:text/html;charset=utf-8,' + encodeURIComponent(htmlContent); - window.open(dataUrl, '_blank', 'width=600,height=400,scrollbars=no,resizable=yes'); - - toaster.toast({ - title: "Browser Helper Opened", - body: "Clipboard helper window opened with auto-copy" - }); - } catch (error) { toaster.toast({ title: "Copy Failed", @@ -230,14 +101,22 @@ export function SmartClipboardButton() { disabled={isLoading} > <div style={{ display: "flex", alignItems: "center", gap: "8px" }}> - {isLoading ? <FaRocket style={{ animation: "spin 1s linear infinite" }} /> : <FaClipboard />} - <div>{isLoading ? "Copying..." : "Smart Clipboard Copy"}</div> + {isLoading ? ( + <FaClipboard style={{ + animation: "pulse 1s ease-in-out infinite", + opacity: 0.7 + }} /> + ) : ( + <FaClipboard /> + )} + <div>{isLoading ? "Copying..." : "Copy Launch Option"}</div> </div> </ButtonItem> <style>{` - @keyframes spin { - from { transform: rotate(0deg); } - to { transform: rotate(360deg); } + @keyframes pulse { + 0% { opacity: 0.7; } + 50% { opacity: 1; } + 100% { opacity: 0.7; } } `}</style> </PanelSectionRow> |
