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 <cursoragent@cursor.com>
This commit is contained in:
cottongin
2026-05-03 01:30:56 -04:00
parent 3bd5752cb5
commit d7a834a992
2 changed files with 49 additions and 5 deletions

49
js/controls.js vendored
View File

@@ -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;
};
}

View File

@@ -764,6 +764,11 @@
<div class="control-row">
<input type="password" id="api-key-input" placeholder="Enter API key">
</div>
<div class="control-row" style="gap: 6px;">
<button id="import-key-btn" title="Import API key from a .txt file">Import from File</button>
<button id="prompt-key-btn" title="Enter API key via dialog (paste works)">Paste Key</button>
<input type="file" id="import-key-file" accept=".txt,.key" style="display: none;">
</div>
<div class="control-row">
<button id="ws-connect-btn">Connect</button>
</div>