summaryrefslogtreecommitdiff
path: root/src/components/SmartClipboardButton.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/SmartClipboardButton.tsx')
-rw-r--r--src/components/SmartClipboardButton.tsx253
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>