-
-
Notifications
You must be signed in to change notification settings - Fork 2
Open
Description
Introduce a new helper script (currently named alfred-chatgpt-opener, though subject to change) designed to open the ChatGPT website, locate the prompt box, and insert content into it.
Directly embedding prompts into the URL works for smaller inputs, but fails with large ones due to HTTP 431 limits. This tool attempts to provide a more reliable solution by opening a browser window and injecting the prompt into the page DOM via AppleScript + JavaScript.
Intended use:
- Primary: integrated into Alfred workflow to support the "Ask ChatGPT (via browser)" flow.
- Secondary: can be invoked outside Alfred for general usage.
Current status:
- Initial hacky WIP / PoC.
- Likely not fully functional yet.
- Includes Chrome + Safari AppleScript flows.
- Supports optional
AUTO_SENDflag to immediately submit after injection (fragile).
Next steps:
- Test and confirm injection across browsers.
- Improve DOM targeting reliability as ChatGPT frontend evolves.
- Refine script interface (flags/env vars).
- Decide on final naming (
alfred-chatgpt-openervs alternatives).
This is the current hacky WIP script:
#!/usr/bin/env zsh
# alfred-chatgpt-paster-targeted.zsh
# Reads prompt from stdin. Creates a new browser window with a unique _alfred_uuid in the URL,
# then injects the full prompt into the ProseMirror editor (id="prompt-textarea") and optionally clicks send.
#
# Env:
# LIMIT (default 8000) - char threshold for URL vs paste
# AUTO_SEND (0 or 1) - if 1, click Send after pasting (fragile)
#
SCRIPT_NAME="${0##*/}"
#LIMIT=${LIMIT:-8000}
LIMIT=${LIMIT:-1}
AUTO_SEND=${AUTO_SEND:-0}
# read prompt from stdin
prompt="$(cat -)"
prompt_trimmed="${prompt%$'\n'}"
len=${#prompt_trimmed}
uuid=$(uuidgen)
base_url="https://chat.openai.com/?_alfred_uuid=${uuid}"
# url-encode helper
urlencode() {
python3 - <<PY - "$1"
import sys, urllib.parse
print(urllib.parse.quote(sys.argv[1], safe=''))
PY
}
if (( len <= LIMIT )); then
encoded_q=$(urlencode "$prompt_trimmed")
open "${base_url}&q=${encoded_q}"
exit 0
fi
b64=$(printf "%s" "$prompt" | base64)
# JavaScript we will inject into the opened tab. It:
# - verifies _alfred_uuid is present
# - decodes base64 payload
# - finds editor by id 'prompt-textarea' or .ProseMirror or the hidden textarea
# - sets content and dispatches input events
# - optionally clicks composer-submit-button to send
js_inject=$(cat <<'JSTEMPLATE'
(function() {
try {
var expectedUuid = '__ALFRED_UUID__';
if (window.location.search.indexOf('_alfred_uuid=' + expectedUuid) === -1) {
console.warn('Alfred: uuid mismatch; aborting paste.');
false;
} else {
var b64 = '__PAYLOAD_B64__';
var raw = '';
try { raw = atob(b64); } catch(e) { console.error('Alfred: base64 decode failed', e); return false; }
// Prefer the ProseMirror contenteditable with known id
var box = document.getElementById('prompt-textarea') || document.querySelector('.ProseMirror') || document.querySelector('textarea[name="prompt-textarea"]');
if (!box) {
console.warn('Alfred: no prompt editor found');
return false;
}
// If it's a hidden textarea (fallback), set value
var isTextArea = (box.tagName && box.tagName.toLowerCase() === 'textarea') || (box.tagName && box.tagName.toLowerCase() === 'input');
var isContentEditable = box.getAttribute && (box.getAttribute('contenteditable') === 'true' || box.classList.contains('ProseMirror'));
// Insert text
try {
if (isContentEditable) {
// Focus and replace text content so ProseMirror picks it up
box.focus();
// Prefer to replace with plain text
if ('innerText' in box) box.innerText = raw;
else box.textContent = raw;
// Dispatch input event — ProseMirror usually listens for this
var ev = new InputEvent('input', { bubbles: true, cancelable: true });
box.dispatchEvent(ev);
} else if (isTextArea) {
box.focus();
box.value = raw;
var ev2 = new Event('input', { bubbles: true });
box.dispatchEvent(ev2);
} else {
// As a last resort, attempt to paste via clipboard API
try {
navigator.clipboard.writeText(raw).then(function(){
// try paste via execCommand (may require focus)
box.focus();
document.execCommand('paste');
});
} catch (e) {
console.warn('Alfred: fallback clipboard paste failed', e);
return false;
}
}
} catch (err) {
console.error('Alfred: insertion error', err);
return false;
}
// Optionally click the send button if requested
var autoSend = __AUTO_SEND_FLAG__;
if (autoSend) {
// try id first, then more general selectors
var sendBtn = document.getElementById('composer-submit-button') || document.querySelector('button[data-testid="send-button"]') || document.querySelector('button[aria-label="Send prompt"]');
if (sendBtn) {
try {
sendBtn.click();
} catch(e) {
// synthetic click failed
console.warn('Alfred: send click attempted but failed', e);
}
} else {
console.warn('Alfred: send button not found for auto-send');
}
}
return true;
}
} catch (e) {
console.error('Alfred: unexpected', e);
return false;
}
})();
JSTEMPLATE
)
# inject uuid, payload, auto_send flag into js_inject safely
js_inject="${js_inject//__ALFRED_UUID__/$uuid}"
js_inject="${js_inject//__PAYLOAD_B64__/$b64}"
js_inject="${js_inject//__AUTO_SEND_FLAG__/$AUTO_SEND}"
# AppleScript for Google Chrome that creates a NEW WINDOW (so we know the tab) and executes the JS
osascript_chrome=$(cat <<APPLESCRIPT
tell application "Google Chrome"
if (count of windows) = 0 then make new window
set newWindow to make new window with properties {URL:"$base_url"}
set theTab to active tab of newWindow
activate
delay 0.35
tell theTab to execute javascript "$js_inject"
end tell
APPLESCRIPT
)
# Attempt Chrome first
if osascript -e 'application "Google Chrome" is running' >/dev/null 2>&1; then
osascript <<APPLESCRIPT
$osascript_chrome
APPLESCRIPT
# Optimistically notify + copy to clipboard in case of race/failure
/usr/bin/osascript -e 'display notification "Pasted prompt to new ChatGPT window. If it failed, it is copied to clipboard." with title "Alfred → ChatGPT"'
printf "%s" "$prompt" | pbcopy
exit 0
fi
# Safari fallback (similar injection)
osascript_safari=$(cat <<APPLESCRIPT
tell application "Safari"
if (count of windows) = 0 then make new document
set newWindow to make new document with properties {URL:"$base_url"}
activate
delay 0.35
tell front document to do JavaScript "$js_inject"
end tell
APPLESCRIPT
)
osascript <<APPLESCRIPT
$osascript_safari
APPLESCRIPT
/usr/bin/osascript -e 'display notification "Pasted prompt to new ChatGPT window (Safari). If it failed, it is copied to clipboard." with title "Alfred → ChatGPT"'
printf "%s" "$prompt" | pbcopy
exit 0Metadata
Metadata
Assignees
Labels
No labels