From 0668428a5ebc221d39b907f251dc0dc43e30a2df Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Mon, 21 Jul 2025 12:28:36 -0400 Subject: testing alt keyboard copy methods --- py_modules/lsfg_vk/plugin.py | 87 +++++- src/api/lsfgApi.ts | 10 + src/components/ClipboardExperiments.tsx | 509 ++++++++++++++++++++++++++++++++ src/components/Content.tsx | 6 + src/components/SmartClipboardButton.tsx | 245 +++++++++++++++ src/components/index.ts | 2 + 6 files changed, 856 insertions(+), 3 deletions(-) create mode 100644 src/components/ClipboardExperiments.tsx create mode 100644 src/components/SmartClipboardButton.tsx diff --git a/py_modules/lsfg_vk/plugin.py b/py_modules/lsfg_vk/plugin.py index 425d7e7..83b318a 100644 --- a/py_modules/lsfg_vk/plugin.py +++ b/py_modules/lsfg_vk/plugin.py @@ -432,7 +432,7 @@ class Plugin: # File content methods async def get_config_file_content(self) -> Dict[str, Any]: - """Get the current config file content + """Get the current TOML configuration file content Returns: Dict containing the config file content or error message @@ -444,7 +444,7 @@ class Plugin: "success": False, "content": None, "path": str(config_path), - "error": "Config file does not exist" + "error": "Configuration file does not exist" } content = config_path.read_text(encoding='utf-8') @@ -469,7 +469,7 @@ class Plugin: Dict containing the launch script content or error message """ try: - script_path = self.installation_service.lsfg_script_path + script_path = self.installation_service.lsfg_launch_script_path if not script_path.exists(): return { "success": False, @@ -493,6 +493,87 @@ 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 74caa57..b984612 100644 --- a/src/api/lsfgApi.ts +++ b/src/api/lsfgApi.ts @@ -36,6 +36,13 @@ 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; @@ -99,6 +106,9 @@ 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 new file mode 100644 index 0000000..ec7d9ab --- /dev/null +++ b/src/components/ClipboardExperiments.tsx @@ -0,0 +1,509 @@ +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([]); + const [isLoading, setIsLoading] = useState(null); + + const addResult = (result: ExperimentResult) => { + setResults(prev => [...prev, { ...result, timestamp: Date.now() }]); + }; + + const getLaunchOptionText = async (): Promise => { + 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 = ` + + + + + Clipboard Helper + + + +
+

๐Ÿš€ Clipboard Automation Test

+

Attempting to copy launch option: ${text}

+
Working...
+
+ +
+ + + + `; + + 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 = ` + + + + + Quick Copy + + + +
+

๐Ÿš€ Clipboard Helper

+

Copying: ${text}

+
โณ Working...
+ + +
+ + + + `; + + 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 ( + <> + +
+ ๐Ÿงช Clipboard Automation Experiments +
+
+ + +
+ Testing different approaches to automate clipboard access in Steam Deck gaming mode: +
+
+ + {/* Test Buttons */} + + +
+ +
Test Direct Clipboard API
+ {isLoading === "direct" &&
โณ
} +
+
+
+ + + +
+ +
Test Data URL Browser
+ {isLoading === "dataurl" &&
โณ
} +
+
+
+ + + +
+ +
Test Input Simulation
+ {isLoading === "input" &&
โณ
} +
+
+
+ + + +
+ +
Test Backend Clipboard
+ {isLoading === "backend" &&
โณ
} +
+
+
+ + + +
+ +
Test Hybrid Approach (Recommended)
+ {isLoading === "hybrid" &&
โณ
} +
+
+
+ + {/* Results Section */} + {results.length > 0 && ( + <> + + +
+
+ {results.filter(r => r.success).length} successful, {results.filter(r => !r.success).length} failed +
+ + Clear + +
+
+
+ + {results.slice(-5).map((result, index) => ( + + +
+
+ {result.success ? "โœ…" : "โŒ"} {result.method} +
+ {result.details && ( +
+ {result.details} +
+ )} + {result.error && ( +
+ Error: {result.error} +
+ )} +
+
+
+ ))} + + )} + + ); +} diff --git a/src/components/Content.tsx b/src/components/Content.tsx index 9ce4c35..f6f24dc 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -8,6 +8,8 @@ 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"; @@ -83,6 +85,10 @@ export function Content() { + + + {/* Experimental Clipboard Automation */} + {/* Plugin Update Checker */} diff --git a/src/components/SmartClipboardButton.tsx b/src/components/SmartClipboardButton.tsx new file mode 100644 index 0000000..229560d --- /dev/null +++ b/src/components/SmartClipboardButton.tsx @@ -0,0 +1,245 @@ +import { useState } from "react"; +import { PanelSectionRow, ButtonItem } from "@decky/ui"; +import { FaClipboard, FaRocket } from "react-icons/fa"; +import { toaster } from "@decky/api"; +import { getLaunchOption, copyToSystemClipboard } from "../api/lsfgApi"; + +export function SmartClipboardButton() { + const [isLoading, setIsLoading] = useState(false); + + const getLaunchOptionText = async (): Promise => { + try { + const result = await getLaunchOption(); + return result.launch_option || "~/lsfg %command%"; + } catch (error) { + return "~/lsfg %command%"; + } + }; + + const copyToClipboard = async () => { + if (isLoading) return; + + setIsLoading(true); + try { + const text = await getLaunchOptionText(); + + // Strategy 1: Try direct navigator.clipboard first (fastest) + let directSuccess = false; + try { + await navigator.clipboard.writeText(text); + // Verify it worked + const readBack = await navigator.clipboard.readText(); + directSuccess = readBack === text; + } catch (e) { + // Direct clipboard failed, will try alternatives + } + + if (directSuccess) { + toaster.toast({ + title: "Copied to Clipboard!", + body: "Launch option ready to paste" + }); + return; + } + + // Strategy 2: Try backend system clipboard + try { + const result = await copyToSystemClipboard(text); + if (result.success) { + toaster.toast({ + title: "Copied to Clipboard!", + body: `Using ${result.method || "system clipboard"}` + }); + return; + } + } catch (e) { + // Backend failed, fall back to browser + } + + // Strategy 3: Fall back to optimized browser window + const htmlContent = ` + + + + + Quick Copy - Steam Deck Clipboard Helper + + + +
+

๐Ÿš€ Steam Deck Clipboard Helper

+
Copy this launch option for your Steam games:
+
${text}
+
โณ Copying to clipboard...
+
+ + +
+
+
+ + + + `; + + 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", + body: `Error: ${String(error)}` + }); + } finally { + setIsLoading(false); + } + }; + + return ( + + +
+ {isLoading ? : } +
{isLoading ? "Copying..." : "Smart Clipboard Copy"}
+
+
+ +
+ ); +} diff --git a/src/components/index.ts b/src/components/index.ts index c0c4804..1a36327 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -5,6 +5,8 @@ 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"; -- cgit v1.2.3 From 404b01b1acd5bd66e009176a71c21c29275fb6b9 Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Mon, 21 Jul 2025 12:48:36 -0400 Subject: rm the other clipboard tests --- py_modules/lsfg_vk/plugin.py | 81 ----- src/api/lsfgApi.ts | 10 - src/components/ClipboardExperiments.tsx | 509 -------------------------------- src/components/Content.tsx | 10 +- src/components/SmartClipboardButton.tsx | 253 +++++----------- src/components/index.ts | 2 - 6 files changed, 67 insertions(+), 798 deletions(-) delete mode 100644 src/components/ClipboardExperiments.tsx 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([]); - const [isLoading, setIsLoading] = useState(null); - - const addResult = (result: ExperimentResult) => { - setResults(prev => [...prev, { ...result, timestamp: Date.now() }]); - }; - - const getLaunchOptionText = async (): Promise => { - 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 = ` - - - - - Clipboard Helper - - - -
-

๐Ÿš€ Clipboard Automation Test

-

Attempting to copy launch option: ${text}

-
Working...
-
- -
- - - - `; - - 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 = ` - - - - - Quick Copy - - - -
-

๐Ÿš€ Clipboard Helper

-

Copying: ${text}

-
โณ Working...
- - -
- - - - `; - - 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 ( - <> - -
- ๐Ÿงช Clipboard Automation Experiments -
-
- - -
- Testing different approaches to automate clipboard access in Steam Deck gaming mode: -
-
- - {/* Test Buttons */} - - -
- -
Test Direct Clipboard API
- {isLoading === "direct" &&
โณ
} -
-
-
- - - -
- -
Test Data URL Browser
- {isLoading === "dataurl" &&
โณ
} -
-
-
- - - -
- -
Test Input Simulation
- {isLoading === "input" &&
โณ
} -
-
-
- - - -
- -
Test Backend Clipboard
- {isLoading === "backend" &&
โณ
} -
-
-
- - - -
- -
Test Hybrid Approach (Recommended)
- {isLoading === "hybrid" &&
โณ
} -
-
-
- - {/* Results Section */} - {results.length > 0 && ( - <> - - -
-
- {results.filter(r => r.success).length} successful, {results.filter(r => !r.success).length} failed -
- - Clear - -
-
-
- - {results.slice(-5).map((result, index) => ( - - -
-
- {result.success ? "โœ…" : "โŒ"} {result.method} -
- {result.details && ( -
- {result.details} -
- )} - {result.error && ( -
- Error: {result.error} -
- )} -
-
-
- ))} - - )} - - ); -} 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() { - - {/* Experimental Clipboard Automation */} - - {/* Plugin Update Checker */} - - - {/* Nerd Stuff Button */} + {/* Nerd Stuff Button */} - - - - Quick Copy - Steam Deck Clipboard Helper - - - -
-

๐Ÿš€ Steam Deck Clipboard Helper

-
Copy this launch option for your Steam games:
-
${text}
-
โณ Copying to clipboard...
-
- - -
-
-
- - - - `; - - 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} >
- {isLoading ? : } -
{isLoading ? "Copying..." : "Smart Clipboard Copy"}
+ {isLoading ? ( + + ) : ( + + )} +
{isLoading ? "Copying..." : "Copy Launch Option"}
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"; -- cgit v1.2.3 From d4c20b6e3a329e4b8988fb52938a346b4cce811e Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Mon, 21 Jul 2025 12:59:43 -0400 Subject: rm unneeded BE changes --- py_modules/lsfg_vk/plugin.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/py_modules/lsfg_vk/plugin.py b/py_modules/lsfg_vk/plugin.py index 980cb53..425d7e7 100644 --- a/py_modules/lsfg_vk/plugin.py +++ b/py_modules/lsfg_vk/plugin.py @@ -432,7 +432,7 @@ class Plugin: # File content methods async def get_config_file_content(self) -> Dict[str, Any]: - """Get the current TOML configuration file content + """Get the current config file content Returns: Dict containing the config file content or error message @@ -444,7 +444,7 @@ class Plugin: "success": False, "content": None, "path": str(config_path), - "error": "Configuration file does not exist" + "error": "Config file does not exist" } content = config_path.read_text(encoding='utf-8') @@ -469,7 +469,7 @@ class Plugin: Dict containing the launch script content or error message """ try: - script_path = self.installation_service.lsfg_launch_script_path + script_path = self.installation_service.lsfg_script_path if not script_path.exists(): return { "success": False, -- cgit v1.2.3 From 4743ebdf9c047a7ce9079026257cad876691cd8d Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Mon, 21 Jul 2025 13:06:38 -0400 Subject: desc and ver bump --- package.json | 2 +- src/components/UsageInstructions.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 2d6fea4..7989109 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "decky-lossless-scaling-vk", - "version": "0.6.7", + "version": "0.6.8", "description": "Use Lossless Scaling on the Steam Deck using the lsfg-vk vulkan layer", "type": "module", "scripts": { diff --git a/src/components/UsageInstructions.tsx b/src/components/UsageInstructions.tsx index 6025241..ce41a53 100644 --- a/src/components/UsageInstructions.tsx +++ b/src/components/UsageInstructions.tsx @@ -33,7 +33,7 @@ export function UsageInstructions({ config }: UsageInstructionsProps) { whiteSpace: "pre-wrap" }} > - Add the launch option below (or use "Launch Option Clipboard") to Steam games to activate frame generation. + Click "Copy Launch Option" below, then paste it into your Steam game's launch options to enable frame generation. -- cgit v1.2.3 From 6a0d2d07f6aa78ae4c669fab2d40b27ad4f3b5b8 Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Mon, 21 Jul 2025 13:14:19 -0400 Subject: restore plugin wiki link for now --- src/components/ClipboardButton.tsx | 6 +++--- src/components/Content.tsx | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/ClipboardButton.tsx b/src/components/ClipboardButton.tsx index 7fd0a9b..cac1863 100644 --- a/src/components/ClipboardButton.tsx +++ b/src/components/ClipboardButton.tsx @@ -1,5 +1,5 @@ import { PanelSectionRow, ButtonItem } from "@decky/ui"; -import { FaClipboard } from "react-icons/fa"; +import { FaBook } from "react-icons/fa"; export function ClipboardButton() { const handleClipboardClick = () => { @@ -13,8 +13,8 @@ export function ClipboardButton() { onClick={handleClipboardClick} >
- -
Launch Option Clipboard
+ +
Plugin Wiki
diff --git a/src/components/Content.tsx b/src/components/Content.tsx index 5f4e139..c4934a6 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -7,6 +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 { PluginUpdateChecker } from "./PluginUpdateChecker"; import { NerdStuffModal } from "./NerdStuffModal"; @@ -82,6 +83,7 @@ export function Content() { + {/* Plugin Update Checker */} -- cgit v1.2.3 From 0dd6b54e6992f6d71a920a7b8bd8ca84672db145 Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Mon, 21 Jul 2025 13:21:11 -0400 Subject: bring copy button to top for more usage --- src/components/Content.tsx | 3 ++- src/components/UsageInstructions.tsx | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/Content.tsx b/src/components/Content.tsx index c4934a6..a075574 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -71,6 +71,8 @@ export function Content() { isInstalled={isInstalled} installationStatus={installationStatus} /> + + {/* Configuration Section - only show if installed */} {isInstalled && ( @@ -84,7 +86,6 @@ export function Content() { - {/* Plugin Update Checker */} {/* Nerd Stuff Button */} diff --git a/src/components/UsageInstructions.tsx b/src/components/UsageInstructions.tsx index ce41a53..bcf258b 100644 --- a/src/components/UsageInstructions.tsx +++ b/src/components/UsageInstructions.tsx @@ -33,7 +33,7 @@ export function UsageInstructions({ config }: UsageInstructionsProps) { whiteSpace: "pre-wrap" }} > - Click "Copy Launch Option" below, then paste it into your Steam game's launch options to enable frame generation. + Click "Copy Launch Option" button, then paste it into your Steam game's launch options to enable frame generation. -- cgit v1.2.3