feat: add Windows support with foreground app detection, clipboard in…#10
Conversation
…tegration, and dynamic command handling
There was a problem hiding this comment.
Pull request overview
This PR expands Stringcast’s cross-platform runtime by adding Windows foreground-app detection, improving robustness around clipboard-based extraction failures, and introducing debounce-based finalization for dynamic triggers (e.g., ?ask: / ?translate:) via a pending-timeout mechanism.
Changes:
- Add Windows foreground application detection (Win32) and document Windows setup/troubleshooting.
- Add dynamic-trigger debounce with timeout-driven finalization, including main-loop refactor to support timeouts.
- Improve clipboard extraction failure cleanup via selection collapse + clipboard restoration, and extend input simulation APIs accordingly.
Reviewed changes
Copilot reviewed 11 out of 12 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/runtime.rs | Exposes pending-timeout handling and updates progress marker used by the runtime pipeline. |
| src/platform/windows.rs | Implements Win32 foreground-app detection with process image lookup and elevation checks. |
| src/pipeline.rs | Adds methods to finalize pending dynamic triggers (buffer + foreground variants) and tests. |
| src/main.rs | Moves event handling to a worker thread and introduces timeout-based processing for pending dynamic triggers. |
| src/input/simulator.rs | Extends InputSimulator with collapse_selection() and implements it for Enigo/recording simulators. |
| src/input/controller.rs | Tracks a pending dynamic-trigger deadline and finalizes pending triggers after debounce. |
| src/extraction/mod.rs | Adds cleanup path for failed extractions to collapse selection and restore clipboard; updates tests. |
| RUNNING.md | Links to the new Windows guide. |
| README.md | Adds Windows documentation link. |
| docs/WINDOWS.md | New Windows installation/usage/troubleshooting guide. |
| Cargo.toml | Adds windows-sys as a Windows-only dependency. |
| Cargo.lock | Locks windows-sys dependency. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| thread::sleep(self.select_all_wait); | ||
| self.input.copy()?; | ||
| self.input.select_all().map_err(ExtractionError::from)?; | ||
| thread::sleep(self.select_all_wait); | ||
| if let Err(error) = self.input.copy() { | ||
| self.cleanup_failed_extraction(&original_clipboard); | ||
| return Err(error.into()); | ||
| } |
| InputEvent::Backspace => { | ||
| self.pending_dynamic_deadline = None; | ||
| self.buffer.backspace(); | ||
| Ok(InputControllerOutcome::BufferUpdated( | ||
| self.buffer.as_str().to_string(), |
| let (event_sender, event_receiver) = mpsc::channel(); | ||
| let worker_log_events = log_events; |
| if event_sender.send((event, received_at)).is_err() { | ||
| eprintln!("Stringcast event error: input worker stopped"); | ||
| } |
| let log_events = log_events(); | ||
| let (event_sender, event_receiver) = mpsc::sync_channel(INPUT_EVENT_QUEUE_CAPACITY); | ||
| let worker_log_events = log_events; | ||
| thread::spawn(move || loop { |
| fn foreground_app() -> Result<ForegroundApp, PlatformContextError> { | ||
| let hwnd = unsafe { GetForegroundWindow() }; | ||
| if hwnd.is_null() { | ||
| return Err(PlatformContextError::Unavailable); | ||
| } | ||
|
|
||
| let process_id = foreground_process_id(hwnd)?; | ||
| let process = ProcessHandle::open(process_id)?; | ||
| let process_image_path = query_process_image_path(process.raw())?; | ||
| let app_id = executable_name(&process_image_path)?; | ||
| let elevated = process_blocks_unelevated_access(process.raw()); | ||
|
|
||
| Ok(ForegroundApp { | ||
| app_id, | ||
| window_id: Some(format!("{:p}", hwnd)), | ||
| display_name: Some(process_image_path), | ||
| secure_input: false, | ||
| elevated, | ||
| }) | ||
| } |
| } | ||
| }; | ||
|
|
||
| self.clipboard.restore(&original_clipboard)?; |
|
thanks @ArushKhasru great work adding Windows support with foreground app detection, improving clipboard extraction error handling with graceful cleanup and implementing dynamic trigger debounce logic for commands like but as you said currently it runs with double ?? instead of single ?, we will check that out in a follow-up issue |
This pull request introduces several improvements and fixes across error handling, input simulation, dynamic trigger processing, and Windows support. The most significant changes enhance robustness during extraction failures, implement dynamic trigger debounce logic, and improve the integration of platform-specific dependencies.
Error handling and extraction robustness:
ClipboardTextExtractor: Ifselect_all,copy, or clipboard read fails during extraction, the code now collapses the selection and restores the clipboard to its original state, preventing lingering selections or corrupted clipboard content. A new helper methodcleanup_failed_extractionwas added for this purpose. [1] [2]Input simulation and interface:
collapse_selectionmethod to theInputSimulatortrait and its implementations. This allows the system to programmatically remove text selections (e.g., by sending a right-arrow keystroke), which is now used for cleanup after extraction failures. [1] [2] [3] [4] [5]Dynamic trigger debounce and pipeline processing:
InputController: When a dynamic trigger is detected, the controller now waits for a configurable debounce interval before finalizing the trigger, improving reliability for triggers that require additional user input. New methods and fields (pending_dynamic_deadline,handle_pending_timeout, etc.) support this logic. [1] [2] [3] [4] [5] [6]TransformationPipelinefor finalizing pending dynamic triggers, including new methods and tests to ensure correct behavior. [1] [2] [3] [4]Main loop and threading improvements:
Platform support and documentation:
windows-sys) toCargo.tomland updated documentation (README.md,RUNNING.md) to reference new Windows setup instructions. [1] [2] [3]