summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxXJSONDeruloXx <danielhimebauch@gmail.com>2025-07-21 12:48:36 -0400
committerxXJSONDeruloXx <danielhimebauch@gmail.com>2025-07-21 12:48:36 -0400
commit404b01b1acd5bd66e009176a71c21c29275fb6b9 (patch)
tree43aa7e442d3774118f5456876668c185863b8d9c
parent0668428a5ebc221d39b907f251dc0dc43e30a2df (diff)
downloaddecky-lsfg-vk-404b01b1acd5bd66e009176a71c21c29275fb6b9.tar.gz
decky-lsfg-vk-404b01b1acd5bd66e009176a71c21c29275fb6b9.zip
rm the other clipboard tests
-rw-r--r--py_modules/lsfg_vk/plugin.py81
-rw-r--r--src/api/lsfgApi.ts10
-rw-r--r--src/components/ClipboardExperiments.tsx509
-rw-r--r--src/components/Content.tsx10
-rw-r--r--src/components/SmartClipboardButton.tsx253
-rw-r--r--src/components/index.ts2
6 files changed, 67 insertions, 798 deletions
diff --git a/py_modules/lsfg_vk/plugin.py b/py_modules/lsfg_vk/plugin.py
index 83b318a..980cb53 100644
--- a/py_modules/lsfg_vk/plugin.py
+++ b/py_modules/lsfg_vk/plugin.py
@@ -493,87 +493,6 @@ class Plugin:
"error": f"Error reading launch script: {str(e)}"
}
- async def copy_to_system_clipboard(self, text: str) -> Dict[str, Any]:
- """Copy text to system clipboard using native tools
-
- Args:
- text: The text to copy to clipboard
-
- Returns:
- Dict indicating success or failure
- """
- try:
- # Detect desktop environment and available clipboard tools
- clipboard_tools = []
-
- # Check for Wayland (wl-clipboard)
- if os.environ.get('WAYLAND_DISPLAY'):
- if self._command_exists('wl-copy'):
- clipboard_tools.append(('wl-copy', lambda t: subprocess.run(['wl-copy'], input=t, text=True, check=True)))
-
- # Check for X11 (xclip or xsel)
- if os.environ.get('DISPLAY'):
- if self._command_exists('xclip'):
- clipboard_tools.append(('xclip', lambda t: subprocess.run(['xclip', '-selection', 'clipboard'], input=t, text=True, check=True)))
- elif self._command_exists('xsel'):
- clipboard_tools.append(('xsel', lambda t: subprocess.run(['xsel', '--clipboard', '--input'], input=t, text=True, check=True)))
-
- # Check for pbcopy (macOS - unlikely on Steam Deck but just in case)
- if self._command_exists('pbcopy'):
- clipboard_tools.append(('pbcopy', lambda t: subprocess.run(['pbcopy'], input=t, text=True, check=True)))
-
- if not clipboard_tools:
- return {
- "success": False,
- "error": "No compatible clipboard tools found (tried: wl-copy, xclip, xsel, pbcopy)",
- "method": None
- }
-
- # Try each clipboard tool until one works
- for tool_name, copy_func in clipboard_tools:
- try:
- copy_func(text)
- return {
- "success": True,
- "method": tool_name,
- "message": f"Successfully copied text using {tool_name}"
- }
- except subprocess.CalledProcessError as e:
- continue # Try next tool
- except Exception as e:
- continue # Try next tool
-
- return {
- "success": False,
- "error": "All clipboard tools failed to copy text",
- "method": None
- }
-
- except Exception as e:
- return {
- "success": False,
- "error": f"Error accessing system clipboard: {str(e)}",
- "method": None
- }
-
- def _command_exists(self, command: str) -> bool:
- """Check if a command exists in the system PATH
-
- Args:
- command: Command name to check
-
- Returns:
- True if command exists, False otherwise
- """
- try:
- subprocess.run(['which', command],
- stdout=subprocess.DEVNULL,
- stderr=subprocess.DEVNULL,
- check=True)
- return True
- except (subprocess.CalledProcessError, FileNotFoundError):
- return False
-
# Lifecycle methods
async def _main(self):
"""
diff --git a/src/api/lsfgApi.ts b/src/api/lsfgApi.ts
index b984612..74caa57 100644
--- a/src/api/lsfgApi.ts
+++ b/src/api/lsfgApi.ts
@@ -36,13 +36,6 @@ export interface DllStatsResult {
error?: string;
}
-export interface ClipboardResult {
- success: boolean;
- method?: string;
- message?: string;
- error?: string;
-}
-
// Use centralized configuration data type
export type LsfgConfig = ConfigurationData;
@@ -106,9 +99,6 @@ export const getLaunchOption = callable<[], LaunchOptionResult>("get_launch_opti
export const getConfigFileContent = callable<[], FileContentResult>("get_config_file_content");
export const getLaunchScriptContent = callable<[], FileContentResult>("get_launch_script_content");
-// Clipboard API
-export const copyToSystemClipboard = callable<[string], ClipboardResult>("copy_to_system_clipboard");
-
// Updated config function using centralized configuration
export const updateLsfgConfig = callable<
[string, number, number, boolean, boolean, string, number, boolean, boolean],
diff --git a/src/components/ClipboardExperiments.tsx b/src/components/ClipboardExperiments.tsx
deleted file mode 100644
index ec7d9ab..0000000
--- a/src/components/ClipboardExperiments.tsx
+++ /dev/null
@@ -1,509 +0,0 @@
-import { useState } from "react";
-import { PanelSectionRow, ButtonItem, Field, Focusable } from "@decky/ui";
-import { FaClipboard, FaFlask, FaRocket, FaCog, FaTerminal } from "react-icons/fa";
-import { toaster } from "@decky/api";
-import { getLaunchOption, copyToSystemClipboard } from "../api/lsfgApi";
-
-interface ExperimentResult {
- success: boolean;
- method: string;
- error?: string;
- details?: string;
-}
-
-export function ClipboardExperiments() {
- const [results, setResults] = useState<ExperimentResult[]>([]);
- const [isLoading, setIsLoading] = useState<string | null>(null);
-
- const addResult = (result: ExperimentResult) => {
- setResults(prev => [...prev, { ...result, timestamp: Date.now() }]);
- };
-
- const getLaunchOptionText = async (): Promise<string> => {
- try {
- const result = await getLaunchOption();
- return result.launch_option || "~/lsfg %command%";
- } catch (error) {
- return "~/lsfg %command%";
- }
- };
-
- // Approach 1: Direct Navigator Clipboard API
- const testDirectClipboard = async () => {
- setIsLoading("direct");
- try {
- const text = await getLaunchOptionText();
- await navigator.clipboard.writeText(text);
-
- // Test if it actually worked by reading back
- const readBack = await navigator.clipboard.readText();
- const success = readBack === text;
-
- addResult({
- success,
- method: "Direct Navigator Clipboard",
- details: success ? `Successfully copied: "${text}"` : `Mismatch: wrote "${text}", read "${readBack}"`
- });
-
- if (success) {
- toaster.toast({
- title: "Clipboard Success!",
- body: "Direct navigator.clipboard API worked"
- });
- }
- } catch (error) {
- addResult({
- success: false,
- method: "Direct Navigator Clipboard",
- error: String(error)
- });
- } finally {
- setIsLoading(null);
- }
- };
-
- // Approach 2: CEF Browser with Data URL
- const testDataUrlApproach = async () => {
- setIsLoading("dataurl");
- try {
- const text = await getLaunchOptionText();
- const htmlContent = `
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="utf-8">
- <title>Clipboard Helper</title>
- <style>
- body {
- font-family: 'Motiva Sans', Arial, sans-serif;
- background: #1e2328;
- color: white;
- padding: 20px;
- text-align: center;
- }
- .content { background: #2a475e; padding: 20px; border-radius: 8px; margin: 20px; }
- .success { color: #66bb6a; font-weight: bold; }
- .error { color: #f44336; font-weight: bold; }
- code { background: rgba(255,255,255,0.1); padding: 4px 8px; border-radius: 4px; }
- </style>
- </head>
- <body>
- <div class="content">
- <h2>๐Ÿš€ Clipboard Automation Test</h2>
- <p>Attempting to copy launch option: <code>${text}</code></p>
- <div id="status">Working...</div>
- <div id="details"></div>
- <button onclick="window.close()" style="margin-top: 20px; padding: 8px 16px;">Close</button>
- </div>
- <script>
- (async function() {
- const statusEl = document.getElementById('status');
- const detailsEl = document.getElementById('details');
- const textToCopy = ${JSON.stringify(text)};
-
- try {
- await navigator.clipboard.writeText(textToCopy);
-
- // Verify it worked
- const readBack = await navigator.clipboard.readText();
- if (readBack === textToCopy) {
- statusEl.innerHTML = '<span class="success">โœ… Success! Text copied to clipboard</span>';
- detailsEl.innerHTML = 'The launch option is now in your clipboard. You can close this window.';
- } else {
- statusEl.innerHTML = '<span class="error">โš ๏ธ Partial Success</span>';
- detailsEl.innerHTML = 'Text was written but verification failed. Check clipboard manually.';
- }
- } catch (error) {
- statusEl.innerHTML = '<span class="error">โŒ Failed</span>';
- detailsEl.innerHTML = 'Error: ' + error.message;
- }
- })();
- </script>
- </body>
- </html>
- `;
-
- const dataUrl = 'data:text/html;charset=utf-8,' + encodeURIComponent(htmlContent);
- window.open(dataUrl, '_blank');
-
- addResult({
- success: true,
- method: "Data URL Browser Window",
- details: "Opened data URL with auto-copy script"
- });
- } catch (error) {
- addResult({
- success: false,
- method: "Data URL Browser Window",
- error: String(error)
- });
- } finally {
- setIsLoading(null);
- }
- };
-
- // Approach 3: Focused Element + Selection + Input API
- const testInputSimulation = async () => {
- setIsLoading("input");
- try {
- const text = await getLaunchOptionText();
-
- // Create a temporary input element
- 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 different copy methods
- let copySuccess = false;
- let method = '';
-
- // Method 1: execCommand (deprecated but might work)
- try {
- if (document.execCommand('copy')) {
- copySuccess = true;
- method = 'execCommand';
- }
- } catch (e) {}
-
- // Method 2: Navigator clipboard on selected text
- if (!copySuccess) {
- try {
- await navigator.clipboard.writeText(text);
- copySuccess = true;
- method = 'navigator.clipboard';
- } catch (e) {}
- }
-
- // Clean up
- document.body.removeChild(tempInput);
-
- if (copySuccess) {
- // Verify
- try {
- const readBack = await navigator.clipboard.readText();
- const verified = readBack === text;
- addResult({
- success: verified,
- method: `Input Simulation (${method})`,
- details: verified ? "Successfully copied and verified" : "Copy worked but verification failed"
- });
- } catch (e) {
- addResult({
- success: true,
- method: `Input Simulation (${method})`,
- details: "Copy appeared to work but couldn't verify"
- });
- }
- } else {
- addResult({
- success: false,
- method: "Input Simulation",
- error: "All copy methods failed"
- });
- }
- } catch (error) {
- addResult({
- success: false,
- method: "Input Simulation",
- error: String(error)
- });
- } finally {
- setIsLoading(null);
- }
- };
-
- // Approach 4: Backend Clipboard
- const testBackendClipboard = async () => {
- setIsLoading("backend");
- try {
- const text = await getLaunchOptionText();
-
- const result = await copyToSystemClipboard(text);
-
- if (result.success) {
- addResult({
- success: true,
- method: `Backend System Clipboard (${result.method})`,
- details: result.message || "Successfully copied to system clipboard"
- });
-
- toaster.toast({
- title: "Clipboard Success!",
- body: `Copied using ${result.method}`
- });
- } else {
- addResult({
- success: false,
- method: "Backend System Clipboard",
- error: result.error || "Unknown error"
- });
- }
- } catch (error) {
- addResult({
- success: false,
- method: "Backend System Clipboard",
- error: String(error)
- });
- } finally {
- setIsLoading(null);
- }
- };
-
- // Approach 5: Hybrid approach with immediate feedback
- const testHybridApproach = async () => {
- setIsLoading("hybrid");
- try {
- const text = await getLaunchOptionText();
-
- // Try direct first
- let directWorked = false;
- try {
- await navigator.clipboard.writeText(text);
- const readBack = await navigator.clipboard.readText();
- directWorked = readBack === text;
- } catch (e) {}
-
- if (directWorked) {
- addResult({
- success: true,
- method: "Hybrid (Direct Success)",
- details: "Direct clipboard API worked, no browser needed"
- });
-
- toaster.toast({
- title: "Clipboard Success!",
- body: "Launch option copied to clipboard"
- });
- } else {
- // Fall back to optimized browser approach
- const htmlContent = `
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="utf-8">
- <title>Quick Copy</title>
- <style>
- body { font-family: system-ui; background: #1a1a1a; color: white; padding: 20px; }
- .container { max-width: 400px; margin: 0 auto; text-align: center; }
- .success { color: #4CAF50; }
- button { padding: 12px 24px; font-size: 16px; margin: 10px; }
- </style>
- </head>
- <body>
- <div class="container">
- <h3>๐Ÿš€ Clipboard Helper</h3>
- <p>Copying: <strong>${text}</strong></p>
- <div id="status">โณ Working...</div>
- <button onclick="copyAndClose()" id="copyBtn">Copy & Close</button>
- <button onclick="window.close()">Just Close</button>
- </div>
- <script>
- const textToCopy = ${JSON.stringify(text)};
- let copied = false;
-
- async function autoCopy() {
- try {
- await navigator.clipboard.writeText(textToCopy);
- document.getElementById('status').innerHTML = '<span class="success">โœ… Copied successfully!</span>';
- copied = true;
- setTimeout(() => window.close(), 1500);
- } catch (e) {
- document.getElementById('status').innerHTML = 'โŒ Auto-copy failed. Use button below.';
- }
- }
-
- async function copyAndClose() {
- try {
- await navigator.clipboard.writeText(textToCopy);
- window.close();
- } catch (e) {
- alert('Copy failed: ' + e.message);
- }
- }
-
- // Auto-copy on load
- autoCopy();
- </script>
- </body>
- </html>
- `;
-
- const dataUrl = 'data:text/html;charset=utf-8,' + encodeURIComponent(htmlContent);
- window.open(dataUrl, '_blank', 'width=500,height=300');
-
- addResult({
- success: true,
- method: "Hybrid (Browser Fallback)",
- details: "Direct failed, opened optimized browser window"
- });
- }
- } catch (error) {
- addResult({
- success: false,
- method: "Hybrid Approach",
- error: String(error)
- });
- } finally {
- setIsLoading(null);
- }
- };
-
- const clearResults = () => {
- setResults([]);
- };
-
- return (
- <>
- <PanelSectionRow>
- <div
- style={{
- fontSize: "14px",
- fontWeight: "bold",
- marginTop: "16px",
- marginBottom: "8px",
- borderBottom: "1px solid rgba(255, 255, 255, 0.2)",
- paddingBottom: "4px",
- color: "white"
- }}
- >
- ๐Ÿงช Clipboard Automation Experiments
- </div>
- </PanelSectionRow>
-
- <PanelSectionRow>
- <div style={{ fontSize: "12px", opacity: 0.8, marginBottom: "8px" }}>
- Testing different approaches to automate clipboard access in Steam Deck gaming mode:
- </div>
- </PanelSectionRow>
-
- {/* Test Buttons */}
- <PanelSectionRow>
- <ButtonItem
- layout="below"
- onClick={testDirectClipboard}
- disabled={isLoading === "direct"}
- >
- <div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
- <FaClipboard />
- <div>Test Direct Clipboard API</div>
- {isLoading === "direct" && <div>โณ</div>}
- </div>
- </ButtonItem>
- </PanelSectionRow>
-
- <PanelSectionRow>
- <ButtonItem
- layout="below"
- onClick={testDataUrlApproach}
- disabled={isLoading === "dataurl"}
- >
- <div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
- <FaRocket />
- <div>Test Data URL Browser</div>
- {isLoading === "dataurl" && <div>โณ</div>}
- </div>
- </ButtonItem>
- </PanelSectionRow>
-
- <PanelSectionRow>
- <ButtonItem
- layout="below"
- onClick={testInputSimulation}
- disabled={isLoading === "input"}
- >
- <div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
- <FaCog />
- <div>Test Input Simulation</div>
- {isLoading === "input" && <div>โณ</div>}
- </div>
- </ButtonItem>
- </PanelSectionRow>
-
- <PanelSectionRow>
- <ButtonItem
- layout="below"
- onClick={testBackendClipboard}
- disabled={isLoading === "backend"}
- >
- <div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
- <FaTerminal />
- <div>Test Backend Clipboard</div>
- {isLoading === "backend" && <div>โณ</div>}
- </div>
- </ButtonItem>
- </PanelSectionRow>
-
- <PanelSectionRow>
- <ButtonItem
- layout="below"
- onClick={testHybridApproach}
- disabled={isLoading === "hybrid"}
- >
- <div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
- <FaFlask />
- <div>Test Hybrid Approach (Recommended)</div>
- {isLoading === "hybrid" && <div>โณ</div>}
- </div>
- </ButtonItem>
- </PanelSectionRow>
-
- {/* Results Section */}
- {results.length > 0 && (
- <>
- <PanelSectionRow>
- <Field
- label={`Test Results (${results.length})`}
- bottomSeparator="none"
- >
- <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
- <div style={{ fontSize: "12px", opacity: 0.8 }}>
- {results.filter(r => r.success).length} successful, {results.filter(r => !r.success).length} failed
- </div>
- <ButtonItem
- layout="inline"
- onClick={clearResults}
- >
- Clear
- </ButtonItem>
- </div>
- </Field>
- </PanelSectionRow>
-
- {results.slice(-5).map((result, index) => (
- <PanelSectionRow key={index}>
- <Focusable>
- <div style={{
- padding: "8px",
- backgroundColor: result.success ? "rgba(76, 175, 80, 0.1)" : "rgba(244, 67, 54, 0.1)",
- borderLeft: `3px solid ${result.success ? "#4CAF50" : "#f44336"}`,
- borderRadius: "4px",
- fontSize: "11px"
- }}>
- <div style={{ fontWeight: "bold", marginBottom: "4px" }}>
- {result.success ? "โœ…" : "โŒ"} {result.method}
- </div>
- {result.details && (
- <div style={{ color: "#4CAF50", marginBottom: "2px" }}>
- {result.details}
- </div>
- )}
- {result.error && (
- <div style={{ color: "#f44336" }}>
- Error: {result.error}
- </div>
- )}
- </div>
- </Focusable>
- </PanelSectionRow>
- ))}
- </>
- )}
- </>
- );
-}
diff --git a/src/components/Content.tsx b/src/components/Content.tsx
index f6f24dc..5f4e139 100644
--- a/src/components/Content.tsx
+++ b/src/components/Content.tsx
@@ -7,9 +7,7 @@ import { InstallationButton } from "./InstallationButton";
import { ConfigurationSection } from "./ConfigurationSection";
import { UsageInstructions } from "./UsageInstructions";
import { WikiButton } from "./WikiButton";
-import { ClipboardButton } from "./ClipboardButton";
import { SmartClipboardButton } from "./SmartClipboardButton";
-import { ClipboardExperiments } from "./ClipboardExperiments";
import { PluginUpdateChecker } from "./PluginUpdateChecker";
import { NerdStuffModal } from "./NerdStuffModal";
import { ConfigurationData } from "../config/configSchema";
@@ -84,16 +82,10 @@ export function Content() {
<UsageInstructions config={config} />
<WikiButton />
- <ClipboardButton />
<SmartClipboardButton />
- {/* Experimental Clipboard Automation */}
- <ClipboardExperiments />
-
{/* Plugin Update Checker */}
- <PluginUpdateChecker />
-
- {/* Nerd Stuff Button */}
+ <PluginUpdateChecker /> {/* Nerd Stuff Button */}
<PanelSectionRow>
<ButtonItem
layout="below"
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>
diff --git a/src/components/index.ts b/src/components/index.ts
index 1a36327..682598c 100644
--- a/src/components/index.ts
+++ b/src/components/index.ts
@@ -4,9 +4,7 @@ export { InstallationButton } from "./InstallationButton";
export { ConfigurationSection } from "./ConfigurationSection";
// export { UsageInstructions } from "./UsageInstructions";
export { WikiButton } from "./WikiButton";
-export { ClipboardButton } from "./ClipboardButton";
export { SmartClipboardButton } from "./SmartClipboardButton";
-export { ClipboardExperiments } from "./ClipboardExperiments";
export { LaunchOptionInfo } from "./LaunchOptionInfo";
export { PluginUpdateChecker } from "./PluginUpdateChecker";
export { NerdStuffModal } from "./NerdStuffModal";