Update to itb_screenshot and friends
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import argparse
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
from selenium.webdriver.common.action_chains import ActionChains
|
||||
|
||||
class InputController:
|
||||
def __init__(self, debug_port: int, verbose: bool = False):
|
||||
"""Initialize connection to Chrome instance"""
|
||||
self.verbose = verbose
|
||||
options = webdriver.ChromeOptions()
|
||||
options.add_experimental_option("debuggerAddress", f"127.0.0.1:{debug_port}")
|
||||
self.driver = webdriver.Chrome(options=options)
|
||||
|
||||
def log(self, msg: str):
|
||||
"""Log message if verbose mode is enabled"""
|
||||
if self.verbose:
|
||||
print(f"DEBUG: {msg}", file=sys.stderr)
|
||||
|
||||
def focus_element(self, element_id: str = None):
|
||||
"""Focus on an input element"""
|
||||
try:
|
||||
if element_id:
|
||||
element = self.driver.find_element(By.ID, element_id)
|
||||
else:
|
||||
# Get currently focused element
|
||||
element = self.driver.switch_to.active_element
|
||||
|
||||
element.click()
|
||||
return element
|
||||
|
||||
except Exception as e:
|
||||
self.log(f"Failed to focus element: {str(e)}")
|
||||
return None
|
||||
|
||||
def enter_text(self, text: str, element_id: str = None, append: bool = False):
|
||||
"""Enter text into an input element"""
|
||||
try:
|
||||
element = self.focus_element(element_id)
|
||||
if not element:
|
||||
return False
|
||||
|
||||
if not append:
|
||||
# Clear existing text first
|
||||
if element.tag_name.lower() in ['input', 'textarea']:
|
||||
element.clear()
|
||||
else:
|
||||
# For contenteditable elements
|
||||
element.send_keys(Keys.CONTROL + 'a')
|
||||
element.send_keys(Keys.DELETE)
|
||||
|
||||
# Type the text
|
||||
element.send_keys(text)
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self.log(f"Failed to enter text: {str(e)}")
|
||||
return False
|
||||
|
||||
def set_caret_position(self, position: int, element_id: str = None):
|
||||
"""Set the text cursor position"""
|
||||
try:
|
||||
element = self.focus_element(element_id)
|
||||
if not element:
|
||||
return False
|
||||
|
||||
# Use JavaScript to set caret position
|
||||
self.driver.execute_script(
|
||||
"""
|
||||
const el = arguments[0];
|
||||
const pos = arguments[1];
|
||||
if (el.setSelectionRange) {
|
||||
el.setSelectionRange(pos, pos);
|
||||
} else if (el.createTextRange) {
|
||||
const range = el.createTextRange();
|
||||
range.collapse(true);
|
||||
range.moveEnd('character', pos);
|
||||
range.moveStart('character', pos);
|
||||
range.select();
|
||||
}
|
||||
""",
|
||||
element, position
|
||||
)
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self.log(f"Failed to set caret position: {str(e)}")
|
||||
return False
|
||||
|
||||
def select_text(self, start: int, end: int, element_id: str = None):
|
||||
"""Select text range in an input element"""
|
||||
try:
|
||||
element = self.focus_element(element_id)
|
||||
if not element:
|
||||
return False
|
||||
|
||||
# Use JavaScript to set selection
|
||||
self.driver.execute_script(
|
||||
"""
|
||||
const el = arguments[0];
|
||||
const start = arguments[1];
|
||||
const end = arguments[2];
|
||||
if (el.setSelectionRange) {
|
||||
el.setSelectionRange(start, end);
|
||||
} else if (el.createTextRange) {
|
||||
const range = el.createTextRange();
|
||||
range.collapse(true);
|
||||
range.moveStart('character', start);
|
||||
range.moveEnd('character', end);
|
||||
range.select();
|
||||
}
|
||||
""",
|
||||
element, start, end
|
||||
)
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self.log(f"Failed to select text: {str(e)}")
|
||||
return False
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Control text input in Chrome')
|
||||
parser.add_argument('debug_port', type=int, help='Chrome debug port')
|
||||
parser.add_argument('-i', '--id', help='Element ID')
|
||||
parser.add_argument('-t', '--text', help='Text to input')
|
||||
parser.add_argument('--append', action='store_true', help='Append text instead of replacing')
|
||||
parser.add_argument('--caret', type=int, help='Set caret position')
|
||||
parser.add_argument('--select-start', type=int, help='Text selection start')
|
||||
parser.add_argument('--select-end', type=int, help='Text selection end')
|
||||
parser.add_argument('-v', '--verbose', action='store_true', help='Enable debug output')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Validate args
|
||||
if args.select_start is not None and args.select_end is None:
|
||||
print("Must specify both --select-start and --select-end", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
if args.select_end is not None and args.select_start is None:
|
||||
print("Must specify both --select-start and --select-end", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
controller = InputController(args.debug_port, args.verbose)
|
||||
success = False
|
||||
|
||||
if args.text is not None:
|
||||
success = controller.enter_text(args.text, args.id, args.append)
|
||||
elif args.caret is not None:
|
||||
success = controller.set_caret_position(args.caret, args.id)
|
||||
elif args.select_start is not None:
|
||||
success = controller.select_text(args.select_start, args.select_end, args.id)
|
||||
else:
|
||||
success = controller.focus_element(args.id) is not None
|
||||
|
||||
sys.exit(0 if success else 1)
|
||||
|
||||
except Exception as e:
|
||||
if args.verbose:
|
||||
print(f"Fatal error: {str(e)}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user