feat(computer-use): Windows UIAutomation backend - #268

Merged
Tryanks merged 8 commits into
mainfrom
feat/123-windows-uia
Aug 24, 2026
Merged

feat(computer-use): Windows UIAutomation backend#268
Tryanks merged 8 commits into
mainfrom
feat/123-windows-uia

Conversation

@Tryanks

Copy link
Copy Markdown
Owner

Implements the last remaining track of #123 — the Windows (UIAutomation) backend for computer-use-mcp. The macOS backend and raw-image fallback already shipped.

Structure (mirrors backend/macos/): backend/windows/{mod,uia,input,capture,pure}.rs.

  • uia.rs — RAII COM init (balanced CoUninitialize; RPC_E_CHANGED_MODE accepted as an existing usable apartment), EnumWindows z-order root enumeration → ElementFromHandle, UIA tree walk into the sharedoutline::UiNode builder (ref/diff identity and the tools layer work unchanged), pattern lookup (Invoke/Toggle/ExpandCollapse/SelectionItem/Value/RangeValue/Scroll/ScrollItem/Legacy).
  • input.rsSendInput: press/click (UIA Invoke first, physical left-click at target-frame center as fallback), set_text (focus + Ctrl+A + unicode), type_text/keypress (UTF-16 KEYEVENTF_UNICODE, surrogate pairs), scroll/drag/move.
  • capture.rsPrintWindow(PW_RENDERFULLCONTENT) + BitBlt fallback → RGBA → PNG (already-resolved png 0.18.1), same text-sparse seam as macOS.
  • pure.rs — control-type→role, pattern→action, root-kind, VK map, rect→Frame: pure fns tested on every platform (mounted via #[path] so macOS/Linux CI runs them).
  • Filter matching hoisted to a shared matches_root_filters (deletes the macOS-private copy).

Dispatch gains Windows arms; macOS is untouched; Linux keeps unsupported. Every HRESULT/Win32 failure is a typed BackendError — no panics in the user's session. bundle_id = process executable filename (no Windows analog; documented).

tcode --cu-smoke — new diagnostic entry (parallels --preview-smoke): enumerates roots + observes the first, prints a summary. Verified live on macOS (enumerated 4 real windows, PASS). This is how the backend gets validated on real hardware.

Verification I ran — native fmt / clippy -D warnings / full workspace tests; clippy/build/test --no-run on x86_64-pc-windows-gnu -D warnings; 24 pure tests; live macOS --cu-smoke.

Real-Windows smoke pending before merge — I'll run --cu-smoke on physical Windows 11 (root enumeration, live UIA observe, a click/type action) via a dev-build artifact and post results here, same as #149.

Part of #123

🤖 Generated with Claude Code

Tryanksand others added 8 commits August 24, 2026 22:24
Implements the Windows track of #123. backend/windows/ mirrors the
macOS backend: uia.rs (COM-init RAII, EnumWindows z-order root
enumeration, UIA tree walk into the shared outline::UiNode, pattern
lookup), input.rs (SendInput keyboard/unicode/mouse/wheel/drag),
capture.rs (PrintWindow+BitBlt → PNG for the raw-image fallback),
pure.rs (control-type→role, pattern→action, VK, rect mappings — pure
fns tested on every platform via #[path]). Filter matching is hoisted
to a shared matches_root_filters used by both backends. Dispatch adds
Windows arms; macOS untouched; Linux keeps unsupported. HRESULT/Win32
failures become typed BackendErrors — no panics in the user session.
Adds : enumerates roots and observes the first,
prints a summary — the on-device validation entry (parallels
--preview-smoke / --cu-permissions). Verified live on macOS.
Part of #123 (CDP roots remain the sole tool-surface deviation).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Observing only the first root landed on a game window (sparse tree by
design); observing all roots surfaces the rich-tree cases too. Still
performs no action, so it cannot disturb a live desktop.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
STA without a message pump made cross-process child navigation return
nothing, collapsing every observed tree to just its root element (real
hardware showed 1 node per window). UIA clients are meant to run in an
MTA; switch CoInitializeEx to COINIT_MULTITHREADED.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…on crate
Replaces ~1,900 lines of hand-rolled UIAutomation COM (which collapsed
every observed tree to its root element on real hardware — an STA
threading pitfall) with a thin adapter over uiautomation 0.25. The
crate owns COM init (MTA), tree walking, control patterns, and
input/screenshot. Kept: the platform-neutral outline vocabulary,
depth/node caps, is_text_sparse + CapturePolicy seam, shared
matches_root_filters, and the pure mapping tables (map.rs, tested on
every platform). A minimal direct windows dep remains only for exe-name
process lookup. Net -227 LOC; no unwrap/expect/panic in the adapter.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
semantic:false is visual mode — it returns a synthetic single root
node by design, so the harness never exercised the tree walk (every
window showed 1 node, on macOS and Windows alike). semantic:true walks
the real accessibility tree.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…de on Linux
matches_root_filters/contains_case_insensitive are only called by the
macOS and Windows backends; Linux compiles neither.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Tryanks
Tryanks enabled auto-merge (squash) August 24, 2026 18:13
@Tryanks
Tryanks merged commit 3d31b3a into mainAug 24, 2026
3 checks passed
@Tryanks
Tryanks deleted the feat/123-windows-uia branch August 24, 2026 18:20
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Tryanks
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

feat(computer-use): Windows UIAutomation backend - #268

Merged
Tryanks merged 8 commits into
mainfrom
feat/123-windows-uia
Aug 24, 2026
Merged

feat(computer-use): Windows UIAutomation backend#268
Tryanks merged 8 commits into
mainfrom
feat/123-windows-uia

Conversation

@Tryanks

Copy link
Copy Markdown
Owner

Implements the last remaining track of #123 — the Windows (UIAutomation) backend for computer-use-mcp. The macOS backend and raw-image fallback already shipped.

Structure (mirrors backend/macos/): backend/windows/{mod,uia,input,capture,pure}.rs.

  • uia.rs — RAII COM init (balanced CoUninitialize; RPC_E_CHANGED_MODE accepted as an existing usable apartment), EnumWindows z-order root enumeration → ElementFromHandle, UIA tree walk into the sharedoutline::UiNode builder (ref/diff identity and the tools layer work unchanged), pattern lookup (Invoke/Toggle/ExpandCollapse/SelectionItem/Value/RangeValue/Scroll/ScrollItem/Legacy).
  • input.rsSendInput: press/click (UIA Invoke first, physical left-click at target-frame center as fallback), set_text (focus + Ctrl+A + unicode), type_text/keypress (UTF-16 KEYEVENTF_UNICODE, surrogate pairs), scroll/drag/move.
  • capture.rsPrintWindow(PW_RENDERFULLCONTENT) + BitBlt fallback → RGBA → PNG (already-resolved png 0.18.1), same text-sparse seam as macOS.
  • pure.rs — control-type→role, pattern→action, root-kind, VK map, rect→Frame: pure fns tested on every platform (mounted via #[path] so macOS/Linux CI runs them).
  • Filter matching hoisted to a shared matches_root_filters (deletes the macOS-private copy).

Dispatch gains Windows arms; macOS is untouched; Linux keeps unsupported. Every HRESULT/Win32 failure is a typed BackendError — no panics in the user's session. bundle_id = process executable filename (no Windows analog; documented).

tcode --cu-smoke — new diagnostic entry (parallels --preview-smoke): enumerates roots + observes the first, prints a summary. Verified live on macOS (enumerated 4 real windows, PASS). This is how the backend gets validated on real hardware.

Verification I ran — native fmt / clippy -D warnings / full workspace tests; clippy/build/test --no-run on x86_64-pc-windows-gnu -D warnings; 24 pure tests; live macOS --cu-smoke.

Real-Windows smoke pending before merge — I'll run --cu-smoke on physical Windows 11 (root enumeration, live UIA observe, a click/type action) via a dev-build artifact and post results here, same as #149.

Part of #123

🤖 Generated with Claude Code

Tryanksand others added 8 commits August 24, 2026 22:24
Implements the Windows track of #123. backend/windows/ mirrors the
macOS backend: uia.rs (COM-init RAII, EnumWindows z-order root
enumeration, UIA tree walk into the shared outline::UiNode, pattern
lookup), input.rs (SendInput keyboard/unicode/mouse/wheel/drag),
capture.rs (PrintWindow+BitBlt → PNG for the raw-image fallback),
pure.rs (control-type→role, pattern→action, VK, rect mappings — pure
fns tested on every platform via #[path]). Filter matching is hoisted
to a shared matches_root_filters used by both backends. Dispatch adds
Windows arms; macOS untouched; Linux keeps unsupported. HRESULT/Win32
failures become typed BackendErrors — no panics in the user session.
Adds : enumerates roots and observes the first,
prints a summary — the on-device validation entry (parallels
--preview-smoke / --cu-permissions). Verified live on macOS.
Part of #123 (CDP roots remain the sole tool-surface deviation).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Observing only the first root landed on a game window (sparse tree by
design); observing all roots surfaces the rich-tree cases too. Still
performs no action, so it cannot disturb a live desktop.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
STA without a message pump made cross-process child navigation return
nothing, collapsing every observed tree to just its root element (real
hardware showed 1 node per window). UIA clients are meant to run in an
MTA; switch CoInitializeEx to COINIT_MULTITHREADED.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…on crate
Replaces ~1,900 lines of hand-rolled UIAutomation COM (which collapsed
every observed tree to its root element on real hardware — an STA
threading pitfall) with a thin adapter over uiautomation 0.25. The
crate owns COM init (MTA), tree walking, control patterns, and
input/screenshot. Kept: the platform-neutral outline vocabulary,
depth/node caps, is_text_sparse + CapturePolicy seam, shared
matches_root_filters, and the pure mapping tables (map.rs, tested on
every platform). A minimal direct windows dep remains only for exe-name
process lookup. Net -227 LOC; no unwrap/expect/panic in the adapter.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
semantic:false is visual mode — it returns a synthetic single root
node by design, so the harness never exercised the tree walk (every
window showed 1 node, on macOS and Windows alike). semantic:true walks
the real accessibility tree.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…de on Linux
matches_root_filters/contains_case_insensitive are only called by the
macOS and Windows backends; Linux compiles neither.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Tryanks
Tryanks enabled auto-merge (squash) August 24, 2026 18:13
@Tryanks
Tryanks merged commit 3d31b3a into mainAug 24, 2026
3 checks passed
@Tryanks
Tryanks deleted the feat/123-windows-uia branch August 24, 2026 18:20
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Tryanks
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(computer-use): Windows UIAutomation backend - #268

Merged
Tryanks merged 8 commits into
mainfrom
feat/123-windows-uia
Aug 24, 2026
Merged

feat(computer-use): Windows UIAutomation backend#268
Tryanks merged 8 commits into
mainfrom
feat/123-windows-uia

Conversation

@Tryanks

Copy link
Copy Markdown
Owner

Implements the last remaining track of #123 — the Windows (UIAutomation) backend for computer-use-mcp. The macOS backend and raw-image fallback already shipped.

Structure (mirrors backend/macos/): backend/windows/{mod,uia,input,capture,pure}.rs.

  • uia.rs — RAII COM init (balanced CoUninitialize; RPC_E_CHANGED_MODE accepted as an existing usable apartment), EnumWindows z-order root enumeration → ElementFromHandle, UIA tree walk into the sharedoutline::UiNode builder (ref/diff identity and the tools layer work unchanged), pattern lookup (Invoke/Toggle/ExpandCollapse/SelectionItem/Value/RangeValue/Scroll/ScrollItem/Legacy).
  • input.rsSendInput: press/click (UIA Invoke first, physical left-click at target-frame center as fallback), set_text (focus + Ctrl+A + unicode), type_text/keypress (UTF-16 KEYEVENTF_UNICODE, surrogate pairs), scroll/drag/move.
  • capture.rsPrintWindow(PW_RENDERFULLCONTENT) + BitBlt fallback → RGBA → PNG (already-resolved png 0.18.1), same text-sparse seam as macOS.
  • pure.rs — control-type→role, pattern→action, root-kind, VK map, rect→Frame: pure fns tested on every platform (mounted via #[path] so macOS/Linux CI runs them).
  • Filter matching hoisted to a shared matches_root_filters (deletes the macOS-private copy).

Dispatch gains Windows arms; macOS is untouched; Linux keeps unsupported. Every HRESULT/Win32 failure is a typed BackendError — no panics in the user's session. bundle_id = process executable filename (no Windows analog; documented).

tcode --cu-smoke — new diagnostic entry (parallels --preview-smoke): enumerates roots + observes the first, prints a summary. Verified live on macOS (enumerated 4 real windows, PASS). This is how the backend gets validated on real hardware.

Verification I ran — native fmt / clippy -D warnings / full workspace tests; clippy/build/test --no-run on x86_64-pc-windows-gnu -D warnings; 24 pure tests; live macOS --cu-smoke.

Real-Windows smoke pending before merge — I'll run --cu-smoke on physical Windows 11 (root enumeration, live UIA observe, a click/type action) via a dev-build artifact and post results here, same as #149.

Part of #123

🤖 Generated with Claude Code

Tryanksand others added 8 commits August 24, 2026 22:24
Implements the Windows track of #123. backend/windows/ mirrors the
macOS backend: uia.rs (COM-init RAII, EnumWindows z-order root
enumeration, UIA tree walk into the shared outline::UiNode, pattern
lookup), input.rs (SendInput keyboard/unicode/mouse/wheel/drag),
capture.rs (PrintWindow+BitBlt → PNG for the raw-image fallback),
pure.rs (control-type→role, pattern→action, VK, rect mappings — pure
fns tested on every platform via #[path]). Filter matching is hoisted
to a shared matches_root_filters used by both backends. Dispatch adds
Windows arms; macOS untouched; Linux keeps unsupported. HRESULT/Win32
failures become typed BackendErrors — no panics in the user session.
Adds : enumerates roots and observes the first,
prints a summary — the on-device validation entry (parallels
--preview-smoke / --cu-permissions). Verified live on macOS.
Part of #123 (CDP roots remain the sole tool-surface deviation).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Observing only the first root landed on a game window (sparse tree by
design); observing all roots surfaces the rich-tree cases too. Still
performs no action, so it cannot disturb a live desktop.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
STA without a message pump made cross-process child navigation return
nothing, collapsing every observed tree to just its root element (real
hardware showed 1 node per window). UIA clients are meant to run in an
MTA; switch CoInitializeEx to COINIT_MULTITHREADED.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…on crate
Replaces ~1,900 lines of hand-rolled UIAutomation COM (which collapsed
every observed tree to its root element on real hardware — an STA
threading pitfall) with a thin adapter over uiautomation 0.25. The
crate owns COM init (MTA), tree walking, control patterns, and
input/screenshot. Kept: the platform-neutral outline vocabulary,
depth/node caps, is_text_sparse + CapturePolicy seam, shared
matches_root_filters, and the pure mapping tables (map.rs, tested on
every platform). A minimal direct windows dep remains only for exe-name
process lookup. Net -227 LOC; no unwrap/expect/panic in the adapter.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
semantic:false is visual mode — it returns a synthetic single root
node by design, so the harness never exercised the tree walk (every
window showed 1 node, on macOS and Windows alike). semantic:true walks
the real accessibility tree.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…de on Linux
matches_root_filters/contains_case_insensitive are only called by the
macOS and Windows backends; Linux compiles neither.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Tryanks
Tryanks enabled auto-merge (squash) August 24, 2026 18:13
@Tryanks
Tryanks merged commit 3d31b3a into mainAug 24, 2026
3 checks passed
@Tryanks
Tryanks deleted the feat/123-windows-uia branch August 24, 2026 18:20
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Tryanks
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(computer-use): Windows UIAutomation backend - #268

Merged
Tryanks merged 8 commits into
mainfrom
feat/123-windows-uia
Aug 24, 2026
Merged

feat(computer-use): Windows UIAutomation backend#268
Tryanks merged 8 commits into
mainfrom
feat/123-windows-uia

Conversation

@Tryanks

Copy link
Copy Markdown
Owner

Implements the last remaining track of #123 — the Windows (UIAutomation) backend for computer-use-mcp. The macOS backend and raw-image fallback already shipped.

Structure (mirrors backend/macos/): backend/windows/{mod,uia,input,capture,pure}.rs.

  • uia.rs — RAII COM init (balanced CoUninitialize; RPC_E_CHANGED_MODE accepted as an existing usable apartment), EnumWindows z-order root enumeration → ElementFromHandle, UIA tree walk into the sharedoutline::UiNode builder (ref/diff identity and the tools layer work unchanged), pattern lookup (Invoke/Toggle/ExpandCollapse/SelectionItem/Value/RangeValue/Scroll/ScrollItem/Legacy).
  • input.rsSendInput: press/click (UIA Invoke first, physical left-click at target-frame center as fallback), set_text (focus + Ctrl+A + unicode), type_text/keypress (UTF-16 KEYEVENTF_UNICODE, surrogate pairs), scroll/drag/move.
  • capture.rsPrintWindow(PW_RENDERFULLCONTENT) + BitBlt fallback → RGBA → PNG (already-resolved png 0.18.1), same text-sparse seam as macOS.
  • pure.rs — control-type→role, pattern→action, root-kind, VK map, rect→Frame: pure fns tested on every platform (mounted via #[path] so macOS/Linux CI runs them).
  • Filter matching hoisted to a shared matches_root_filters (deletes the macOS-private copy).

Dispatch gains Windows arms; macOS is untouched; Linux keeps unsupported. Every HRESULT/Win32 failure is a typed BackendError — no panics in the user's session. bundle_id = process executable filename (no Windows analog; documented).

tcode --cu-smoke — new diagnostic entry (parallels --preview-smoke): enumerates roots + observes the first, prints a summary. Verified live on macOS (enumerated 4 real windows, PASS). This is how the backend gets validated on real hardware.

Verification I ran — native fmt / clippy -D warnings / full workspace tests; clippy/build/test --no-run on x86_64-pc-windows-gnu -D warnings; 24 pure tests; live macOS --cu-smoke.

Real-Windows smoke pending before merge — I'll run --cu-smoke on physical Windows 11 (root enumeration, live UIA observe, a click/type action) via a dev-build artifact and post results here, same as #149.

Part of #123

🤖 Generated with Claude Code

Tryanksand others added 8 commits August 24, 2026 22:24
Implements the Windows track of #123. backend/windows/ mirrors the
macOS backend: uia.rs (COM-init RAII, EnumWindows z-order root
enumeration, UIA tree walk into the shared outline::UiNode, pattern
lookup), input.rs (SendInput keyboard/unicode/mouse/wheel/drag),
capture.rs (PrintWindow+BitBlt → PNG for the raw-image fallback),
pure.rs (control-type→role, pattern→action, VK, rect mappings — pure
fns tested on every platform via #[path]). Filter matching is hoisted
to a shared matches_root_filters used by both backends. Dispatch adds
Windows arms; macOS untouched; Linux keeps unsupported. HRESULT/Win32
failures become typed BackendErrors — no panics in the user session.
Adds : enumerates roots and observes the first,
prints a summary — the on-device validation entry (parallels
--preview-smoke / --cu-permissions). Verified live on macOS.
Part of #123 (CDP roots remain the sole tool-surface deviation).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Observing only the first root landed on a game window (sparse tree by
design); observing all roots surfaces the rich-tree cases too. Still
performs no action, so it cannot disturb a live desktop.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
STA without a message pump made cross-process child navigation return
nothing, collapsing every observed tree to just its root element (real
hardware showed 1 node per window). UIA clients are meant to run in an
MTA; switch CoInitializeEx to COINIT_MULTITHREADED.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…on crate
Replaces ~1,900 lines of hand-rolled UIAutomation COM (which collapsed
every observed tree to its root element on real hardware — an STA
threading pitfall) with a thin adapter over uiautomation 0.25. The
crate owns COM init (MTA), tree walking, control patterns, and
input/screenshot. Kept: the platform-neutral outline vocabulary,
depth/node caps, is_text_sparse + CapturePolicy seam, shared
matches_root_filters, and the pure mapping tables (map.rs, tested on
every platform). A minimal direct windows dep remains only for exe-name
process lookup. Net -227 LOC; no unwrap/expect/panic in the adapter.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
semantic:false is visual mode — it returns a synthetic single root
node by design, so the harness never exercised the tree walk (every
window showed 1 node, on macOS and Windows alike). semantic:true walks
the real accessibility tree.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…de on Linux
matches_root_filters/contains_case_insensitive are only called by the
macOS and Windows backends; Linux compiles neither.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Tryanks
Tryanks enabled auto-merge (squash) August 24, 2026 18:13
@Tryanks
Tryanks merged commit 3d31b3a into mainAug 24, 2026
3 checks passed
@Tryanks
Tryanks deleted the feat/123-windows-uia branch August 24, 2026 18:20
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Tryanks
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

feat(computer-use): Windows UIAutomation backend - #268

Merged
Tryanks merged 8 commits into
mainfrom
feat/123-windows-uia
Aug 24, 2026
Merged

feat(computer-use): Windows UIAutomation backend#268
Tryanks merged 8 commits into
mainfrom
feat/123-windows-uia

Conversation

@Tryanks

Copy link
Copy Markdown
Owner

Implements the last remaining track of #123 — the Windows (UIAutomation) backend for computer-use-mcp. The macOS backend and raw-image fallback already shipped.

Structure (mirrors backend/macos/): backend/windows/{mod,uia,input,capture,pure}.rs.

  • uia.rs — RAII COM init (balanced CoUninitialize; RPC_E_CHANGED_MODE accepted as an existing usable apartment), EnumWindows z-order root enumeration → ElementFromHandle, UIA tree walk into the sharedoutline::UiNode builder (ref/diff identity and the tools layer work unchanged), pattern lookup (Invoke/Toggle/ExpandCollapse/SelectionItem/Value/RangeValue/Scroll/ScrollItem/Legacy).
  • input.rsSendInput: press/click (UIA Invoke first, physical left-click at target-frame center as fallback), set_text (focus + Ctrl+A + unicode), type_text/keypress (UTF-16 KEYEVENTF_UNICODE, surrogate pairs), scroll/drag/move.
  • capture.rsPrintWindow(PW_RENDERFULLCONTENT) + BitBlt fallback → RGBA → PNG (already-resolved png 0.18.1), same text-sparse seam as macOS.
  • pure.rs — control-type→role, pattern→action, root-kind, VK map, rect→Frame: pure fns tested on every platform (mounted via #[path] so macOS/Linux CI runs them).
  • Filter matching hoisted to a shared matches_root_filters (deletes the macOS-private copy).

Dispatch gains Windows arms; macOS is untouched; Linux keeps unsupported. Every HRESULT/Win32 failure is a typed BackendError — no panics in the user's session. bundle_id = process executable filename (no Windows analog; documented).

tcode --cu-smoke — new diagnostic entry (parallels --preview-smoke): enumerates roots + observes the first, prints a summary. Verified live on macOS (enumerated 4 real windows, PASS). This is how the backend gets validated on real hardware.

Verification I ran — native fmt / clippy -D warnings / full workspace tests; clippy/build/test --no-run on x86_64-pc-windows-gnu -D warnings; 24 pure tests; live macOS --cu-smoke.

Real-Windows smoke pending before merge — I'll run --cu-smoke on physical Windows 11 (root enumeration, live UIA observe, a click/type action) via a dev-build artifact and post results here, same as #149.

Part of #123

🤖 Generated with Claude Code

Tryanksand others added 8 commits August 24, 2026 22:24
Implements the Windows track of #123. backend/windows/ mirrors the
macOS backend: uia.rs (COM-init RAII, EnumWindows z-order root
enumeration, UIA tree walk into the shared outline::UiNode, pattern
lookup), input.rs (SendInput keyboard/unicode/mouse/wheel/drag),
capture.rs (PrintWindow+BitBlt → PNG for the raw-image fallback),
pure.rs (control-type→role, pattern→action, VK, rect mappings — pure
fns tested on every platform via #[path]). Filter matching is hoisted
to a shared matches_root_filters used by both backends. Dispatch adds
Windows arms; macOS untouched; Linux keeps unsupported. HRESULT/Win32
failures become typed BackendErrors — no panics in the user session.
Adds : enumerates roots and observes the first,
prints a summary — the on-device validation entry (parallels
--preview-smoke / --cu-permissions). Verified live on macOS.
Part of #123 (CDP roots remain the sole tool-surface deviation).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Observing only the first root landed on a game window (sparse tree by
design); observing all roots surfaces the rich-tree cases too. Still
performs no action, so it cannot disturb a live desktop.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
STA without a message pump made cross-process child navigation return
nothing, collapsing every observed tree to just its root element (real
hardware showed 1 node per window). UIA clients are meant to run in an
MTA; switch CoInitializeEx to COINIT_MULTITHREADED.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…on crate
Replaces ~1,900 lines of hand-rolled UIAutomation COM (which collapsed
every observed tree to its root element on real hardware — an STA
threading pitfall) with a thin adapter over uiautomation 0.25. The
crate owns COM init (MTA), tree walking, control patterns, and
input/screenshot. Kept: the platform-neutral outline vocabulary,
depth/node caps, is_text_sparse + CapturePolicy seam, shared
matches_root_filters, and the pure mapping tables (map.rs, tested on
every platform). A minimal direct windows dep remains only for exe-name
process lookup. Net -227 LOC; no unwrap/expect/panic in the adapter.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
semantic:false is visual mode — it returns a synthetic single root
node by design, so the harness never exercised the tree walk (every
window showed 1 node, on macOS and Windows alike). semantic:true walks
the real accessibility tree.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…de on Linux
matches_root_filters/contains_case_insensitive are only called by the
macOS and Windows backends; Linux compiles neither.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Tryanks
Tryanks enabled auto-merge (squash) August 24, 2026 18:13
@Tryanks
Tryanks merged commit 3d31b3a into mainAug 24, 2026
3 checks passed
@Tryanks
Tryanks deleted the feat/123-windows-uia branch August 24, 2026 18:20
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Tryanks
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(computer-use): Windows UIAutomation backend - #268

Merged
Tryanks merged 8 commits into
mainfrom
feat/123-windows-uia
Aug 24, 2026
Merged

feat(computer-use): Windows UIAutomation backend#268
Tryanks merged 8 commits into
mainfrom
feat/123-windows-uia

Conversation

@Tryanks

Copy link
Copy Markdown
Owner

Implements the last remaining track of #123 — the Windows (UIAutomation) backend for computer-use-mcp. The macOS backend and raw-image fallback already shipped.

Structure (mirrors backend/macos/): backend/windows/{mod,uia,input,capture,pure}.rs.

  • uia.rs — RAII COM init (balanced CoUninitialize; RPC_E_CHANGED_MODE accepted as an existing usable apartment), EnumWindows z-order root enumeration → ElementFromHandle, UIA tree walk into the sharedoutline::UiNode builder (ref/diff identity and the tools layer work unchanged), pattern lookup (Invoke/Toggle/ExpandCollapse/SelectionItem/Value/RangeValue/Scroll/ScrollItem/Legacy).
  • input.rsSendInput: press/click (UIA Invoke first, physical left-click at target-frame center as fallback), set_text (focus + Ctrl+A + unicode), type_text/keypress (UTF-16 KEYEVENTF_UNICODE, surrogate pairs), scroll/drag/move.
  • capture.rsPrintWindow(PW_RENDERFULLCONTENT) + BitBlt fallback → RGBA → PNG (already-resolved png 0.18.1), same text-sparse seam as macOS.
  • pure.rs — control-type→role, pattern→action, root-kind, VK map, rect→Frame: pure fns tested on every platform (mounted via #[path] so macOS/Linux CI runs them).
  • Filter matching hoisted to a shared matches_root_filters (deletes the macOS-private copy).

Dispatch gains Windows arms; macOS is untouched; Linux keeps unsupported. Every HRESULT/Win32 failure is a typed BackendError — no panics in the user's session. bundle_id = process executable filename (no Windows analog; documented).

tcode --cu-smoke — new diagnostic entry (parallels --preview-smoke): enumerates roots + observes the first, prints a summary. Verified live on macOS (enumerated 4 real windows, PASS). This is how the backend gets validated on real hardware.

Verification I ran — native fmt / clippy -D warnings / full workspace tests; clippy/build/test --no-run on x86_64-pc-windows-gnu -D warnings; 24 pure tests; live macOS --cu-smoke.

Real-Windows smoke pending before merge — I'll run --cu-smoke on physical Windows 11 (root enumeration, live UIA observe, a click/type action) via a dev-build artifact and post results here, same as #149.

Part of #123

🤖 Generated with Claude Code

Tryanksand others added 8 commits August 24, 2026 22:24
Implements the Windows track of #123. backend/windows/ mirrors the
macOS backend: uia.rs (COM-init RAII, EnumWindows z-order root
enumeration, UIA tree walk into the shared outline::UiNode, pattern
lookup), input.rs (SendInput keyboard/unicode/mouse/wheel/drag),
capture.rs (PrintWindow+BitBlt → PNG for the raw-image fallback),
pure.rs (control-type→role, pattern→action, VK, rect mappings — pure
fns tested on every platform via #[path]). Filter matching is hoisted
to a shared matches_root_filters used by both backends. Dispatch adds
Windows arms; macOS untouched; Linux keeps unsupported. HRESULT/Win32
failures become typed BackendErrors — no panics in the user session.
Adds : enumerates roots and observes the first,
prints a summary — the on-device validation entry (parallels
--preview-smoke / --cu-permissions). Verified live on macOS.
Part of #123 (CDP roots remain the sole tool-surface deviation).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Observing only the first root landed on a game window (sparse tree by
design); observing all roots surfaces the rich-tree cases too. Still
performs no action, so it cannot disturb a live desktop.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
STA without a message pump made cross-process child navigation return
nothing, collapsing every observed tree to just its root element (real
hardware showed 1 node per window). UIA clients are meant to run in an
MTA; switch CoInitializeEx to COINIT_MULTITHREADED.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…on crate
Replaces ~1,900 lines of hand-rolled UIAutomation COM (which collapsed
every observed tree to its root element on real hardware — an STA
threading pitfall) with a thin adapter over uiautomation 0.25. The
crate owns COM init (MTA), tree walking, control patterns, and
input/screenshot. Kept: the platform-neutral outline vocabulary,
depth/node caps, is_text_sparse + CapturePolicy seam, shared
matches_root_filters, and the pure mapping tables (map.rs, tested on
every platform). A minimal direct windows dep remains only for exe-name
process lookup. Net -227 LOC; no unwrap/expect/panic in the adapter.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
semantic:false is visual mode — it returns a synthetic single root
node by design, so the harness never exercised the tree walk (every
window showed 1 node, on macOS and Windows alike). semantic:true walks
the real accessibility tree.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…de on Linux
matches_root_filters/contains_case_insensitive are only called by the
macOS and Windows backends; Linux compiles neither.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Tryanks
Tryanks enabled auto-merge (squash) August 24, 2026 18:13
@Tryanks
Tryanks merged commit 3d31b3a into mainAug 24, 2026
3 checks passed
@Tryanks
Tryanks deleted the feat/123-windows-uia branch August 24, 2026 18:20
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Tryanks
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(computer-use): Windows UIAutomation backend - #268

Merged
Tryanks merged 8 commits into
mainfrom
feat/123-windows-uia
Aug 24, 2026
Merged

feat(computer-use): Windows UIAutomation backend#268
Tryanks merged 8 commits into
mainfrom
feat/123-windows-uia

Conversation

@Tryanks

Copy link
Copy Markdown
Owner

Implements the last remaining track of #123 — the Windows (UIAutomation) backend for computer-use-mcp. The macOS backend and raw-image fallback already shipped.

Structure (mirrors backend/macos/): backend/windows/{mod,uia,input,capture,pure}.rs.

  • uia.rs — RAII COM init (balanced CoUninitialize; RPC_E_CHANGED_MODE accepted as an existing usable apartment), EnumWindows z-order root enumeration → ElementFromHandle, UIA tree walk into the sharedoutline::UiNode builder (ref/diff identity and the tools layer work unchanged), pattern lookup (Invoke/Toggle/ExpandCollapse/SelectionItem/Value/RangeValue/Scroll/ScrollItem/Legacy).
  • input.rsSendInput: press/click (UIA Invoke first, physical left-click at target-frame center as fallback), set_text (focus + Ctrl+A + unicode), type_text/keypress (UTF-16 KEYEVENTF_UNICODE, surrogate pairs), scroll/drag/move.
  • capture.rsPrintWindow(PW_RENDERFULLCONTENT) + BitBlt fallback → RGBA → PNG (already-resolved png 0.18.1), same text-sparse seam as macOS.
  • pure.rs — control-type→role, pattern→action, root-kind, VK map, rect→Frame: pure fns tested on every platform (mounted via #[path] so macOS/Linux CI runs them).
  • Filter matching hoisted to a shared matches_root_filters (deletes the macOS-private copy).

Dispatch gains Windows arms; macOS is untouched; Linux keeps unsupported. Every HRESULT/Win32 failure is a typed BackendError — no panics in the user's session. bundle_id = process executable filename (no Windows analog; documented).

tcode --cu-smoke — new diagnostic entry (parallels --preview-smoke): enumerates roots + observes the first, prints a summary. Verified live on macOS (enumerated 4 real windows, PASS). This is how the backend gets validated on real hardware.

Verification I ran — native fmt / clippy -D warnings / full workspace tests; clippy/build/test --no-run on x86_64-pc-windows-gnu -D warnings; 24 pure tests; live macOS --cu-smoke.

Real-Windows smoke pending before merge — I'll run --cu-smoke on physical Windows 11 (root enumeration, live UIA observe, a click/type action) via a dev-build artifact and post results here, same as #149.

Part of #123

🤖 Generated with Claude Code

Tryanksand others added 8 commits August 24, 2026 22:24
Implements the Windows track of #123. backend/windows/ mirrors the
macOS backend: uia.rs (COM-init RAII, EnumWindows z-order root
enumeration, UIA tree walk into the shared outline::UiNode, pattern
lookup), input.rs (SendInput keyboard/unicode/mouse/wheel/drag),
capture.rs (PrintWindow+BitBlt → PNG for the raw-image fallback),
pure.rs (control-type→role, pattern→action, VK, rect mappings — pure
fns tested on every platform via #[path]). Filter matching is hoisted
to a shared matches_root_filters used by both backends. Dispatch adds
Windows arms; macOS untouched; Linux keeps unsupported. HRESULT/Win32
failures become typed BackendErrors — no panics in the user session.
Adds : enumerates roots and observes the first,
prints a summary — the on-device validation entry (parallels
--preview-smoke / --cu-permissions). Verified live on macOS.
Part of #123 (CDP roots remain the sole tool-surface deviation).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Observing only the first root landed on a game window (sparse tree by
design); observing all roots surfaces the rich-tree cases too. Still
performs no action, so it cannot disturb a live desktop.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
STA without a message pump made cross-process child navigation return
nothing, collapsing every observed tree to just its root element (real
hardware showed 1 node per window). UIA clients are meant to run in an
MTA; switch CoInitializeEx to COINIT_MULTITHREADED.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…on crate
Replaces ~1,900 lines of hand-rolled UIAutomation COM (which collapsed
every observed tree to its root element on real hardware — an STA
threading pitfall) with a thin adapter over uiautomation 0.25. The
crate owns COM init (MTA), tree walking, control patterns, and
input/screenshot. Kept: the platform-neutral outline vocabulary,
depth/node caps, is_text_sparse + CapturePolicy seam, shared
matches_root_filters, and the pure mapping tables (map.rs, tested on
every platform). A minimal direct windows dep remains only for exe-name
process lookup. Net -227 LOC; no unwrap/expect/panic in the adapter.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
semantic:false is visual mode — it returns a synthetic single root
node by design, so the harness never exercised the tree walk (every
window showed 1 node, on macOS and Windows alike). semantic:true walks
the real accessibility tree.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…de on Linux
matches_root_filters/contains_case_insensitive are only called by the
macOS and Windows backends; Linux compiles neither.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Tryanks
Tryanks enabled auto-merge (squash) August 24, 2026 18:13
@Tryanks
Tryanks merged commit 3d31b3a into mainAug 24, 2026
3 checks passed
@Tryanks
Tryanks deleted the feat/123-windows-uia branch August 24, 2026 18:20
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Tryanks
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

feat(computer-use): Windows UIAutomation backend - #268

Merged
Tryanks merged 8 commits into
mainfrom
feat/123-windows-uia
Aug 24, 2026
Merged

feat(computer-use): Windows UIAutomation backend#268
Tryanks merged 8 commits into
mainfrom
feat/123-windows-uia

Conversation

@Tryanks

Copy link
Copy Markdown
Owner

Implements the last remaining track of #123 — the Windows (UIAutomation) backend for computer-use-mcp. The macOS backend and raw-image fallback already shipped.

Structure (mirrors backend/macos/): backend/windows/{mod,uia,input,capture,pure}.rs.

  • uia.rs — RAII COM init (balanced CoUninitialize; RPC_E_CHANGED_MODE accepted as an existing usable apartment), EnumWindows z-order root enumeration → ElementFromHandle, UIA tree walk into the sharedoutline::UiNode builder (ref/diff identity and the tools layer work unchanged), pattern lookup (Invoke/Toggle/ExpandCollapse/SelectionItem/Value/RangeValue/Scroll/ScrollItem/Legacy).
  • input.rsSendInput: press/click (UIA Invoke first, physical left-click at target-frame center as fallback), set_text (focus + Ctrl+A + unicode), type_text/keypress (UTF-16 KEYEVENTF_UNICODE, surrogate pairs), scroll/drag/move.
  • capture.rsPrintWindow(PW_RENDERFULLCONTENT) + BitBlt fallback → RGBA → PNG (already-resolved png 0.18.1), same text-sparse seam as macOS.
  • pure.rs — control-type→role, pattern→action, root-kind, VK map, rect→Frame: pure fns tested on every platform (mounted via #[path] so macOS/Linux CI runs them).
  • Filter matching hoisted to a shared matches_root_filters (deletes the macOS-private copy).

Dispatch gains Windows arms; macOS is untouched; Linux keeps unsupported. Every HRESULT/Win32 failure is a typed BackendError — no panics in the user's session. bundle_id = process executable filename (no Windows analog; documented).

tcode --cu-smoke — new diagnostic entry (parallels --preview-smoke): enumerates roots + observes the first, prints a summary. Verified live on macOS (enumerated 4 real windows, PASS). This is how the backend gets validated on real hardware.

Verification I ran — native fmt / clippy -D warnings / full workspace tests; clippy/build/test --no-run on x86_64-pc-windows-gnu -D warnings; 24 pure tests; live macOS --cu-smoke.

Real-Windows smoke pending before merge — I'll run --cu-smoke on physical Windows 11 (root enumeration, live UIA observe, a click/type action) via a dev-build artifact and post results here, same as #149.

Part of #123

🤖 Generated with Claude Code

Tryanksand others added 8 commits August 24, 2026 22:24
Implements the Windows track of #123. backend/windows/ mirrors the
macOS backend: uia.rs (COM-init RAII, EnumWindows z-order root
enumeration, UIA tree walk into the shared outline::UiNode, pattern
lookup), input.rs (SendInput keyboard/unicode/mouse/wheel/drag),
capture.rs (PrintWindow+BitBlt → PNG for the raw-image fallback),
pure.rs (control-type→role, pattern→action, VK, rect mappings — pure
fns tested on every platform via #[path]). Filter matching is hoisted
to a shared matches_root_filters used by both backends. Dispatch adds
Windows arms; macOS untouched; Linux keeps unsupported. HRESULT/Win32
failures become typed BackendErrors — no panics in the user session.
Adds : enumerates roots and observes the first,
prints a summary — the on-device validation entry (parallels
--preview-smoke / --cu-permissions). Verified live on macOS.
Part of #123 (CDP roots remain the sole tool-surface deviation).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Observing only the first root landed on a game window (sparse tree by
design); observing all roots surfaces the rich-tree cases too. Still
performs no action, so it cannot disturb a live desktop.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
STA without a message pump made cross-process child navigation return
nothing, collapsing every observed tree to just its root element (real
hardware showed 1 node per window). UIA clients are meant to run in an
MTA; switch CoInitializeEx to COINIT_MULTITHREADED.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…on crate
Replaces ~1,900 lines of hand-rolled UIAutomation COM (which collapsed
every observed tree to its root element on real hardware — an STA
threading pitfall) with a thin adapter over uiautomation 0.25. The
crate owns COM init (MTA), tree walking, control patterns, and
input/screenshot. Kept: the platform-neutral outline vocabulary,
depth/node caps, is_text_sparse + CapturePolicy seam, shared
matches_root_filters, and the pure mapping tables (map.rs, tested on
every platform). A minimal direct windows dep remains only for exe-name
process lookup. Net -227 LOC; no unwrap/expect/panic in the adapter.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
semantic:false is visual mode — it returns a synthetic single root
node by design, so the harness never exercised the tree walk (every
window showed 1 node, on macOS and Windows alike). semantic:true walks
the real accessibility tree.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…de on Linux
matches_root_filters/contains_case_insensitive are only called by the
macOS and Windows backends; Linux compiles neither.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Tryanks
Tryanks enabled auto-merge (squash) August 24, 2026 18:13
@Tryanks
Tryanks merged commit 3d31b3a into mainAug 24, 2026
3 checks passed
@Tryanks
Tryanks deleted the feat/123-windows-uia branch August 24, 2026 18:20
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Tryanks