summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorxXJSONDeruloXx <danielhimebauch@gmail.com>2025-09-17 15:03:57 -0400
committerxXJSONDeruloXx <danielhimebauch@gmail.com>2025-09-17 15:03:57 -0400
commit227acb969dc4b36cae427d2cf6a063ec51f41e13 (patch)
tree8d8e41ec4b4503bd666b6b1662fa82921b54043c /src
parentbac59e55e319d871de5ac70c47199eb209fee66e (diff)
downloaddecky-lsfg-vk-227acb969dc4b36cae427d2cf6a063ec51f41e13.tar.gz
decky-lsfg-vk-227acb969dc4b36cae427d2cf6a063ec51f41e13.zip
feat: add clipboard readout and logging
Diffstat (limited to 'src')
-rw-r--r--src/components/ClipboardDisplay.tsx139
-rw-r--r--src/components/Content.tsx2
-rw-r--r--src/components/index.ts1
3 files changed, 142 insertions, 0 deletions
diff --git a/src/components/ClipboardDisplay.tsx b/src/components/ClipboardDisplay.tsx
new file mode 100644
index 0000000..852a50f
--- /dev/null
+++ b/src/components/ClipboardDisplay.tsx
@@ -0,0 +1,139 @@
+import { useState, useEffect } from "react";
+import { PanelSectionRow } from "@decky/ui";
+import { FaClipboard, FaEye } from "react-icons/fa";
+
+export function ClipboardDisplay() {
+ const [clipboardContent, setClipboardContent] = useState<string>("");
+ const [isReading, setIsReading] = useState(false);
+
+ const readClipboard = async () => {
+ if (isReading) return; // Prevent concurrent reads
+
+ setIsReading(true);
+ try {
+ console.log("ClipboardDisplay: Attempting to read clipboard...");
+
+ if (!navigator.clipboard) {
+ console.log("ClipboardDisplay: navigator.clipboard not available");
+ setClipboardContent("Clipboard API not available");
+ return;
+ }
+
+ if (!navigator.clipboard.readText) {
+ console.log("ClipboardDisplay: navigator.clipboard.readText not available");
+ setClipboardContent("Clipboard read not supported");
+ return;
+ }
+
+ console.log("ClipboardDisplay: Calling navigator.clipboard.readText()...");
+ const content = await navigator.clipboard.readText();
+ console.log("ClipboardDisplay: Successfully read clipboard:", content.length, "characters");
+ setClipboardContent(content);
+ } catch (error) {
+ // This is expected if user hasn't granted clipboard permissions
+ // or if we're in a context where reading isn't allowed
+ console.log("ClipboardDisplay: Error reading clipboard:", error);
+ console.log("ClipboardDisplay: Error name:", (error as Error).name);
+ console.log("ClipboardDisplay: Error message:", (error as Error).message);
+
+ // More specific error messages based on error type
+ if (error instanceof DOMException) {
+ switch (error.name) {
+ case 'NotAllowedError':
+ setClipboardContent("Clipboard access denied - check permissions");
+ break;
+ case 'NotFoundError':
+ setClipboardContent("No clipboard data found");
+ break;
+ case 'SecurityError':
+ setClipboardContent("Clipboard access blocked by security policy");
+ break;
+ default:
+ setClipboardContent(`Clipboard error: ${error.name}`);
+ }
+ } else {
+ setClipboardContent("Unable to read clipboard");
+ }
+ } finally {
+ setIsReading(false);
+ }
+ };
+
+ // Read clipboard on mount and then every 3 seconds
+ useEffect(() => {
+ readClipboard();
+
+ const interval = setInterval(() => {
+ readClipboard();
+ }, 3000);
+
+ return () => clearInterval(interval);
+ }, []);
+
+ const truncateText = (text: string, maxLength: number = 60) => {
+ if (text.length <= maxLength) return text;
+ return text.substring(0, maxLength) + "...";
+ };
+
+ const displayText = truncateText(clipboardContent);
+
+ return (
+ <PanelSectionRow>
+ <div style={{
+ padding: "8px",
+ backgroundColor: "rgba(255, 255, 255, 0.05)",
+ borderRadius: "4px",
+ border: "1px solid rgba(255, 255, 255, 0.1)",
+ marginBottom: "8px"
+ }}>
+ <div style={{
+ display: "flex",
+ alignItems: "center",
+ gap: "8px",
+ marginBottom: "4px"
+ }}>
+ <FaClipboard style={{ color: "#888", fontSize: "12px" }} />
+ <div style={{
+ fontSize: "12px",
+ fontWeight: "bold",
+ color: "#888",
+ textTransform: "uppercase"
+ }}>
+ Current Clipboard
+ </div>
+ {isReading && (
+ <FaEye style={{
+ color: "#888",
+ fontSize: "10px",
+ animation: "pulse 1s ease-in-out infinite"
+ }} />
+ )}
+ </div>
+ <div style={{
+ fontSize: "11px",
+ color: clipboardContent.includes("error") ||
+ clipboardContent.includes("denied") ||
+ clipboardContent.includes("not available") ||
+ clipboardContent.includes("not supported") ||
+ clipboardContent.includes("blocked") ||
+ clipboardContent === "Unable to read clipboard"
+ ? "#ff6b6b"
+ : "#fff",
+ fontFamily: "monospace",
+ wordBreak: "break-word",
+ lineHeight: "1.3",
+ minHeight: "14px"
+ }}>
+ {displayText || "Reading clipboard..."}
+ </div>
+ </div>
+ <style>{`
+ @keyframes pulse {
+ 0% { opacity: 0.5; }
+ 50% { opacity: 1; }
+ 100% { opacity: 0.5; }
+ }
+ `}</style>
+ </PanelSectionRow>
+ );
+}
diff --git a/src/components/Content.tsx b/src/components/Content.tsx
index e870dda..e82a7c3 100644
--- a/src/components/Content.tsx
+++ b/src/components/Content.tsx
@@ -12,6 +12,7 @@ import { WikiButton } from "./WikiButton";
import { ClipboardButton } from "./ClipboardButton";
import { SmartClipboardButton } from "./SmartClipboardButton";
import { FgmodClipboardButton } from "./FgmodClipboardButton";
+import { ClipboardDisplay } from "./ClipboardDisplay";
import { PluginUpdateChecker } from "./PluginUpdateChecker";
import { NerdStuffModal } from "./NerdStuffModal";
import { ConfigurationData } from "../config/configSchema";
@@ -100,6 +101,7 @@ export function Content() {
{/* Clipboard buttons - only show if installed */}
{isInstalled && (
<>
+ <ClipboardDisplay />
<SmartClipboardButton />
<FgmodClipboardButton />
</>
diff --git a/src/components/index.ts b/src/components/index.ts
index 32bbb31..c3ace84 100644
--- a/src/components/index.ts
+++ b/src/components/index.ts
@@ -7,6 +7,7 @@ export { UsageInstructions } from "./UsageInstructions";
export { WikiButton } from "./WikiButton";
export { SmartClipboardButton } from "./SmartClipboardButton";
export { FgmodClipboardButton } from "./FgmodClipboardButton";
+export { ClipboardDisplay } from "./ClipboardDisplay";
export { PluginUpdateChecker } from "./PluginUpdateChecker";
export { NerdStuffModal } from "./NerdStuffModal";
export { ProfileManagement } from "./ProfileManagement";