Submit prompt to the page with retries and keyboard fallbacks.
(
self, prompt: str, image_list: List, check_client_disconnected: Callable
)
| 137 | await enable_temporary_chat_mode(self.page) |
| 138 | |
| 139 | async def submit_prompt( |
| 140 | self, prompt: str, image_list: List, check_client_disconnected: Callable |
| 141 | ): |
| 142 | """Submit prompt to the page with retries and keyboard fallbacks.""" |
| 143 | max_retries = 2 |
| 144 | for attempt in range(max_retries): |
| 145 | try: |
| 146 | self.logger.info( |
| 147 | f"[{self.req_id}] Filling and submitting prompt (Attempt {attempt + 1}/{max_retries})..." |
| 148 | ) |
| 149 | textarea = self.page.locator(PROMPT_TEXTAREA_SELECTOR) |
| 150 | await expect_async(textarea).to_be_visible(timeout=10000) |
| 151 | await self._check_disconnect( |
| 152 | check_client_disconnected, "After Input Visible" |
| 153 | ) |
| 154 | |
| 155 | # Fill textarea using centralized logic (inherited from InputController if possible, or direct) |
| 156 | await textarea.evaluate( |
| 157 | "(el, t) => { el.value = t; el.dispatchEvent(new Event('input', {bubbles:true})); el.dispatchEvent(new Event('change', {bubbles:true})); }", |
| 158 | prompt, |
| 159 | ) |
| 160 | await self._check_disconnect( |
| 161 | check_client_disconnected, "After Input Fill" |
| 162 | ) |
| 163 | |
| 164 | if image_list: |
| 165 | await self._open_upload_menu_and_choose_file(image_list) |
| 166 | |
| 167 | # Wait for submit button to be enabled |
| 168 | submit = self.page.locator(SUBMIT_BUTTON_SELECTOR) |
| 169 | button_clicked = False |
| 170 | is_btn_enabled = False |
| 171 | try: |
| 172 | await expect_async(submit).to_be_enabled(timeout=10000) |
| 173 | is_btn_enabled = True |
| 174 | except Exception: |
| 175 | self.logger.warning( |
| 176 | f"[{self.req_id}] Submit button not enabled within timeout, trying keyboard fallback." |
| 177 | ) |
| 178 | |
| 179 | await self._check_disconnect( |
| 180 | check_client_disconnected, "After Submit Button Check" |
| 181 | ) |
| 182 | |
| 183 | if is_btn_enabled: |
| 184 | try: |
| 185 | # Defensive workarounds before click: handle dialogs, backdrops and tooltips |
| 186 | await self._handle_post_upload_dialog() |
| 187 | await self._dismiss_backdrops() |
| 188 | if hasattr(self, "_dismiss_tooltip_overlays"): |
| 189 | await self._dismiss_tooltip_overlays() |
| 190 | |
| 191 | await submit.click(timeout=5000) |
| 192 | button_clicked = True |
| 193 | self.logger.info(f"[{self.req_id}] Submit button clicked.") |
| 194 | await check_quota_limit(self.page, self.req_id) |
| 195 | except QuotaExceededError: |
| 196 | raise |