From de676a9311c6d766f9317c17d79ab509ee19173d Mon Sep 17 00:00:00 2001 From: admin Date: Fri, 15 May 2026 19:06:48 -0700 Subject: [PATCH 1/3] Filter UI chrome out of cmd.exe / Windows Terminal review output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the focused UWP window hosts a TermControl child (cmd.exe and other shells running inside Windows Terminal), obtainUWPWindowText() previously appended every non-None child name during the tree walk. That pulled in the window title repeated as child element names, the "Close Tab" button, the four scroll-bar parts ("Vertical Small/Large Decrease/Increase"), and the "System" menu — all interleaved with the actual terminal text. Detect TermControl during the walk; when one is present anywhere in the foreground window, return only [title, terminal text] and drop the chrome. Non-terminal UWP apps fall through to the existing behavior unchanged. --- addon/globalPlugins/virtualRevision.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/addon/globalPlugins/virtualRevision.py b/addon/globalPlugins/virtualRevision.py index c4ab909..a37555e 100644 --- a/addon/globalPlugins/virtualRevision.py +++ b/addon/globalPlugins/virtualRevision.py @@ -18,18 +18,24 @@ except: SCRCAT_TEXTREVIEW = None +def _isTermControl(obj): + return hasattr(obj, "UIAElement") and obj.UIAElement and obj.UIAElement.currentClassName == "TermControl" + def obtainUWPWindowText(): foreground = api.getForegroundObject() desktop = api.getDesktopObject() uwpTextList = [foreground.name] + termTextList = [] + hasTerm = _isTermControl(foreground) curObject=foreground.firstChild while curObject: - if curObject.name is not None: uwpTextList.append(curObject.name) - if hasattr(curObject, "UIAElement") and curObject.UIAElement and curObject.UIAElement.currentClassName == "TermControl": + if _isTermControl(curObject): + hasTerm = True info = curObject.makeTextInfo(textInfos.POSITION_FIRST) info.expand(textInfos.UNIT_STORY) - text = info.clipboardText.rstrip() - uwpTextList.append(text) + termTextList.append(info.clipboardText.rstrip()) + elif curObject.name is not None: + uwpTextList.append(curObject.name) if curObject.simpleFirstChild: curObject=curObject.simpleFirstChild continue @@ -49,11 +55,16 @@ def obtainUWPWindowText(): curObject=parent.simpleNext except AttributeError: continue - if hasattr(foreground, "UIAElement") and foreground.UIAElement and foreground.UIAElement.currentClassName == "TermControl": + if _isTermControl(foreground): + hasTerm = True info = foreground.makeTextInfo(textInfos.POSITION_FIRST) info.expand(textInfos.UNIT_STORY) - text = info.clipboardText.rstrip() - uwpTextList.append(text) + termTextList.append(info.clipboardText.rstrip()) + # For terminal-hosted windows (e.g. cmd.exe inside Windows Terminal), the child walk + # also picks up the duplicated title, scroll-bar parts, "Close Tab" and "System" menu. + # Suppress that chrome and return only the title plus the actual terminal text. + if hasTerm: + return [foreground.name] + termTextList return uwpTextList class GlobalPlugin(globalPluginHandler.GlobalPlugin): From a742f7a62758aafef8ddbec79264053e69aa5748 Mon Sep 17 00:00:00 2001 From: admin Date: Fri, 15 May 2026 19:28:31 -0700 Subject: [PATCH 2/3] Collapse blank-line padding from terminal review output In addition to the chrome elements stripped above, capturing the full TermControl story also returns space-padded blank rows for any visible terminal cell that hasn't been written to. TUI apps such as Claude Code, vim, htop, lazygit etc. paint content at the top of the buffer and a status/input row at the bottom, leaving a large empty band in the middle that becomes a long run of blank lines in the review window. Add _cleanTerminalText() that: - Strips per-line trailing whitespace (removes the column padding) - Collapses 2+ consecutive blank lines into a single blank line - rstrips the final result This preserves intentional single-blank-line spacing (e.g. the blank between "All rights reserved." and the cmd prompt) while eliminating the long visual gap, and tightens cmd output as a side benefit. --- addon/globalPlugins/virtualRevision.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/addon/globalPlugins/virtualRevision.py b/addon/globalPlugins/virtualRevision.py index a37555e..5bb7b7a 100644 --- a/addon/globalPlugins/virtualRevision.py +++ b/addon/globalPlugins/virtualRevision.py @@ -21,6 +21,24 @@ def _isTermControl(obj): return hasattr(obj, "UIAElement") and obj.UIAElement and obj.UIAElement.currentClassName == "TermControl" +def _cleanTerminalText(text): + # Terminal cells are space-padded to the column width, and TUI apps such as Claude Code, + # vim, htop, etc. reserve a fixed bottom row for status/input — the rows in between are + # captured as long runs of blank padding. Strip per-line trailing whitespace and collapse + # 2+ consecutive blank lines to one so the review buffer doesn't include the visual gap. + lines = [line.rstrip() for line in text.split("\n")] + cleaned = [] + prevBlank = False + for line in lines: + if not line: + if prevBlank: + continue + prevBlank = True + else: + prevBlank = False + cleaned.append(line) + return "\n".join(cleaned).rstrip() + def obtainUWPWindowText(): foreground = api.getForegroundObject() desktop = api.getDesktopObject() @@ -33,7 +51,7 @@ def obtainUWPWindowText(): hasTerm = True info = curObject.makeTextInfo(textInfos.POSITION_FIRST) info.expand(textInfos.UNIT_STORY) - termTextList.append(info.clipboardText.rstrip()) + termTextList.append(_cleanTerminalText(info.clipboardText)) elif curObject.name is not None: uwpTextList.append(curObject.name) if curObject.simpleFirstChild: @@ -59,7 +77,7 @@ def obtainUWPWindowText(): hasTerm = True info = foreground.makeTextInfo(textInfos.POSITION_FIRST) info.expand(textInfos.UNIT_STORY) - termTextList.append(info.clipboardText.rstrip()) + termTextList.append(_cleanTerminalText(info.clipboardText)) # For terminal-hosted windows (e.g. cmd.exe inside Windows Terminal), the child walk # also picks up the duplicated title, scroll-bar parts, "Close Tab" and "System" menu. # Suppress that chrome and return only the title plus the actual terminal text. From 80778ef23ea44099829f9cd60c697f8920fa0356 Mon Sep 17 00:00:00 2001 From: admin Date: Fri, 15 May 2026 19:35:06 -0700 Subject: [PATCH 3/3] Drop decorative and blank lines from terminal review output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous commit collapsed runs of blank padding to a single blank line, but in TUI apps that paint boxed UIs (Claude Code, lazygit, gh dash) every box-row gap also produces "│ │" lines that aren't blank and so survive the collapse. Reviewing the result line-by-line still required pressing arrow keys through dozens of decorative rows between content paragraphs. Replace the collapse logic with a stricter filter: after per-line rstrip, drop any line whose only non-whitespace characters are in the Unicode Box Drawing block (U+2500-U+257F). Truly blank lines fall under the same predicate (empty stripped == decorative) so they're removed too. Content lines that include "│" borders survive because the letters between the borders fall outside the range. Tradeoff: cmd output loses its single-blank line between the copyright banner and the prompt. For the audience most affected (NVDA users navigating the review window with up/down arrow), eliminating the decorative-line wading is the bigger win. --- addon/globalPlugins/virtualRevision.py | 38 +++++++++++++++----------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/addon/globalPlugins/virtualRevision.py b/addon/globalPlugins/virtualRevision.py index 5bb7b7a..7369ded 100644 --- a/addon/globalPlugins/virtualRevision.py +++ b/addon/globalPlugins/virtualRevision.py @@ -21,23 +21,29 @@ def _isTermControl(obj): return hasattr(obj, "UIAElement") and obj.UIAElement and obj.UIAElement.currentClassName == "TermControl" +def _isDecorativeTerminalLine(line): + # True for lines whose only non-whitespace characters are in the Unicode Box Drawing + # block (U+2500-U+257F): the borders, separators, and corners that TUI apps such as + # Claude Code paint. Content lines that happen to contain a "│" border still survive + # because the letters/digits inside fall outside the range. + stripped = line.strip() + if not stripped: + return True + return all("─" <= ch <= "╿" for ch in stripped) + def _cleanTerminalText(text): - # Terminal cells are space-padded to the column width, and TUI apps such as Claude Code, - # vim, htop, etc. reserve a fixed bottom row for status/input — the rows in between are - # captured as long runs of blank padding. Strip per-line trailing whitespace and collapse - # 2+ consecutive blank lines to one so the review buffer doesn't include the visual gap. - lines = [line.rstrip() for line in text.split("\n")] - cleaned = [] - prevBlank = False - for line in lines: - if not line: - if prevBlank: - continue - prevBlank = True - else: - prevBlank = False - cleaned.append(line) - return "\n".join(cleaned).rstrip() + # Terminal cells are space-padded to the column width, and TUI apps reserve fixed + # rows/columns for borders, status, input, etc. Captured as a UNIT_STORY this turns + # into long runs of blank padding and box-border-only lines that bury the actual + # content when navigated line-by-line in the review window. Strip per-line trailing + # whitespace and drop lines that carry no real content. + lines = [] + for line in text.split("\n"): + stripped = line.rstrip() + if _isDecorativeTerminalLine(stripped): + continue + lines.append(stripped) + return "\n".join(lines) def obtainUWPWindowText(): foreground = api.getForegroundObject()