From d7a834a9924fb033cff2b4a97f861114e6a88b9a Mon Sep 17 00:00:00 2001 From: cottongin Date: Sun, 3 May 2026 01:30:56 -0400 Subject: [PATCH] feat: add file import and prompt() methods for API key entry in OBS OBS Browser Source lacks clipboard support, making it impractical to enter long API keys. Add two workarounds: "Import from File" reads a key from a .txt file via the native file picker, and "Paste Key" uses window.prompt() where OS-level paste should work. Co-authored-by: Cursor --- js/controls.js | 49 ++++++++++++++++++++++++++++++++++++----- optimized-controls.html | 5 +++++ 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/js/controls.js b/js/controls.js index 69d0d1b..7c4e07d 100644 --- a/js/controls.js +++ b/js/controls.js @@ -194,6 +194,42 @@ function initControls(manager, wsClient, components) { localStorage.setItem(STORAGE_API_KEY, apiKeyInput.value.trim()); }); + const importKeyBtn = document.getElementById('import-key-btn'); + const importKeyFile = /** @type {HTMLInputElement | null} */ ( + document.getElementById('import-key-file') + ); + const promptKeyBtn = document.getElementById('prompt-key-btn'); + + function applyImportedKey(key) { + if (!apiKeyInput) return; + apiKeyInput.value = key; + localStorage.setItem(STORAGE_API_KEY, key); + } + + if (importKeyBtn && importKeyFile) { + importKeyBtn.addEventListener('click', () => { + importKeyFile.value = ''; + importKeyFile.click(); + }); + importKeyFile.addEventListener('change', () => { + const file = importKeyFile.files && importKeyFile.files[0]; + if (!file) return; + var reader = new FileReader(); + reader.onload = function () { + var text = typeof reader.result === 'string' ? reader.result.trim() : ''; + if (text) applyImportedKey(text); + }; + reader.readAsText(file); + }); + } + + if (promptKeyBtn) { + promptKeyBtn.addEventListener('click', () => { + var key = window.prompt('Paste or type your API key:'); + if (key && key.trim()) applyImportedKey(key.trim()); + }); + } + const volumeSlider = document.getElementById('volume-slider'); const volumeValue = document.getElementById('volume-value'); const themeSound = /** @type {HTMLAudioElement | null} */ ( @@ -340,22 +376,25 @@ function initConnectionStatusHandler() { const wsDisconnectRow = document.getElementById('ws-disconnect-row'); const apiUrlInput = document.getElementById('api-url-input'); const apiKeyInput = document.getElementById('api-key-input'); + const importKeyBtn = document.getElementById('import-key-btn'); + const promptKeyBtn = document.getElementById('prompt-key-btn'); return (state, message) => { if (wsStatusDot) wsStatusDot.className = `status-dot ${state}`; if (wsStatusText) wsStatusText.textContent = message ?? String(state); - if (state === 'connected') { + var isConnected = state === 'connected'; + if (isConnected) { if (wsConnectBtn) wsConnectBtn.style.display = 'none'; if (wsDisconnectRow) wsDisconnectRow.style.display = 'flex'; - if (apiUrlInput) apiUrlInput.disabled = true; - if (apiKeyInput) apiKeyInput.disabled = true; } else { if (wsConnectBtn) wsConnectBtn.style.display = 'block'; if (wsDisconnectRow) wsDisconnectRow.style.display = 'none'; - if (apiUrlInput) apiUrlInput.disabled = false; - if (apiKeyInput) apiKeyInput.disabled = false; } + if (apiUrlInput) apiUrlInput.disabled = isConnected; + if (apiKeyInput) apiKeyInput.disabled = isConnected; + if (importKeyBtn) importKeyBtn.disabled = isConnected; + if (promptKeyBtn) promptKeyBtn.disabled = isConnected; }; } diff --git a/optimized-controls.html b/optimized-controls.html index 332cc34..34f8f60 100644 --- a/optimized-controls.html +++ b/optimized-controls.html @@ -764,6 +764,11 @@
+
+ + + +