Skip to content

fix(code): scope selection copy to the clicked screen - #5140

Merged
Mason Daugherty (mdrxy) merged 2 commits into
mainfrom
mdrxy/code/scope-selection-copy-to-active-screen
Jul 29, 2026
Merged

fix(code): scope selection copy to the clicked screen#5140
Mason Daugherty (mdrxy) merged 2 commits into
mainfrom
mdrxy/code/scope-selection-copy-to-active-screen

Conversation

@mdrxy

Copy link
Copy Markdown
Member

Clicking a copyable value in a modal (for example the thread id in the Debug Console) no longer fights with text still selected in the conversation behind it — only the value you clicked lands on the clipboard.


Text selected with the mouse is auto-copied on mouse release, and that scan walked App.query("*"). Textual roots App.query at the app's default (compose) screen rather than the active one, so a selection left behind in the transcript stayed in scope even while a modal was on top. Any click inside the modal then produced two clipboard writes — the modal's own copy plus the stale transcript selection — and whichever landed last won.

Selections are per-screen state, so the scan is now scoped to a single screen: copy_selection_to_clipboard takes an optional keyword-only screen (defaulting to the app's active screen), and the mouse-up handler pins it to the screen the release actually landed on. Background selections are left untouched and visible, matching the current behavior; they simply aren't copied by clicks that happened somewhere else. As a side effect, selections made inside a modal are now copyable, which the app-rooted scan never reached.

The new regression tests drive a real selection through Textual's event path (triple-click via Pilot) with a modal pushed on top, and fail against the previous implementation.

Made by Open SWE

Auto-copy on mouse-up scanned `App.query("*")`, which Textual roots at the
app's default (compose) screen. A selection left on the transcript therefore
kept being copied by clicks inside a modal, racing that modal's own copy
action (e.g. the Debug Console thread id) for the clipboard.

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
@github-actions github-actions Bot added dcode Related to `deepagents-code` fix A bug fix (PATCH) internal User is a member of the `langchain-ai` GitHub organization size: S 50-199 LOC labels Jul 29, 2026
@mdrxy
Mason Daugherty (mdrxy) marked this pull request as ready for review July 29, 2026 14:43

@open-swe open-swe Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Open SWE Review: No issues found

Open SWE reviewed this PR and found no potential bugs to report.

Open in WebView Open SWE trace

Follow-up to the screen-scoping fix, from PR review.

`copy_selection_to_clipboard` took `screen: Screen | None = None` and fell
back to `app.screen`. That fallback was late-bound: it resolved the active
screen at callback time, which is the exact semantics the scoping fix
removed, so any future caller that omitted the argument would silently
reintroduce the bug. Make `screen` required and drop the branch.

Both empty-`screen_stack` guards go with it. Neither was reachable:
`App.on_event` already dereferences `self.screen` unguarded to forward the
`MouseUp`, and the only code that empties the stack runs after the message
loop has stopped. Mutants deleting either guard killed no test.

Close the coverage gap that mattered. The suite asserted only that a
selection below a modal is *not* copied, never that the modal's own
selection *is* — so an over-correction skipping modal screens entirely
passed all 25 tests while breaking the feature the fix exists to protect
(selecting the Debug Console thread id). Add a test covering both
directions; that mutant now fails. Replace the default-screen test with one
that passes a non-active screen while both screens hold selections, so it
actually exercises the arg rather than coinciding with the old behavior.

Docstrings stated conclusions rather than mechanisms: "the screen the
release landed on" implied hit-testing when `self.screen` is simply
top-of-stack, and the rationale leaned on private `App._get_dom_base`.
Restate both in terms of Textual routing mouse events only to the top of
the stack, and name the reproducible symptom (a spurious "copied" toast).
@mdrxy
Mason Daugherty (mdrxy) merged commit 4de6037 into main Jul 29, 2026
55 checks passed
@mdrxy
Mason Daugherty (mdrxy) deleted the mdrxy/code/scope-selection-copy-to-active-screen branch July 29, 2026 17:53
Marcelo5444 pushed a commit to Marcelo5444/deepagents that referenced this pull request Jul 30, 2026
)

Clicking a copyable value in a modal (for example the thread id in the
Debug Console) no longer fights with text still selected in the
conversation behind it — only the value you clicked lands on the
clipboard.

---

Text selected with the mouse is auto-copied on mouse release, and that
scan walked `App.query("*")`. Textual roots `App.query` at the app's
*default* (compose) screen rather than the active one, so a selection
left behind in the transcript stayed in scope even while a modal was on
top. Any click inside the modal then produced two clipboard writes — the
modal's own copy plus the stale transcript selection — and whichever
landed last won.

Selections are per-screen state, so the scan is now scoped to a single
screen: `copy_selection_to_clipboard` takes an optional keyword-only
`screen` (defaulting to the app's active screen), and the mouse-up
handler pins it to the screen the release actually landed on. Background
selections are left untouched and visible, matching the current
behavior; they simply aren't copied by clicks that happened somewhere
else. As a side effect, selections made *inside* a modal are now
copyable, which the app-rooted scan never reached.

The new regression tests drive a real selection through Textual's event
path (triple-click via `Pilot`) with a modal pushed on top, and fail
against the previous implementation.

Made by [Open
SWE](https://openswe.vercel.app/agents/bde43330-a625-d287-611f-af90f64dac82)

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Mason Daugherty (mdrxy) pushed a commit that referenced this pull request Jul 30, 2026
> [!CAUTION]
> Merging this PR will automatically publish to **PyPI** and create a
**GitHub release**.

For the full release process, see
[`.github/RELEASING.md`](https://github.com/langchain-ai/deepagents/blob/main/.github/RELEASING.md).

---

_Release notes preview: keep this section in sync with the package
`CHANGELOG.md`. Publish reads the merged CHANGELOG via `release.yml`,
not this PR description — keep them aligned anyway so the PR stays an
accurate historical record for reviewers and anyone returning later._

---


##
[0.1.50](deepagents-code==0.1.49...deepagents-code==0.1.50)
(2026-07-30)

### Highlights

- Added project hooks workspace trust and expanded Hooks v2 support with
client and server lifecycle events plus runtime feedback
([#5105](#5105),
[#5104](#5104),
[#4997](#4997),
[#5045](#5045)).
- Added an option to mute the “YOLO is active” toast
([#5103](#5103)).
- Made the splash screen `thread` ID clickable to copy it
([#5173](#5173)).
- Show `ask_user` answers directly on the answered tool row
([#5100](#5100)).
- Show a toast when submitting an empty required `ask_user` answer
([#5095](#5095)).
- Added thread message counts to the Debug Console
([#5117](#5117)).

### Fixes and improvements

- Gated Hooks v2 behind `DEEPAGENTS_CODE_EXPERIMENTAL` and improved hook
resume stability across identity and Command tool results
([#5146](#5146),
[#5176](#5176)).
- Kept server hook state out of task results
([#5164](#5164)).
- Stopped duplicate Auto transcript events during interrupt replay
([#5157](#5157)).
- Kept `/update` and `/install --package` prompts responsive
([#5127](#5127)).
- Refreshed the `/threads` cache after each turn
([#5174](#5174)).
- Anchored toasts above the chat input and added a toast when media is
dropped into a free-text question
([#5101](#5101),
[#5099](#5099)).
- Improved thread status message styling and links
([#5118](#5118)).
- Made resume hints echo the launched command name
([#5119](#5119)).
- Scoped selection copy to the clicked screen
([#5140](#5140)).
- Ignored mouse hits on detached widgets
([#5114](#5114)).

_End release notes preview._

---

> [!NOTE]
> A **New Contributors** section is appended to the GitHub release notes
automatically at publish time (see [Release
Pipeline](https://github.com/langchain-ai/deepagents/blob/main/.github/RELEASING.md#release-pipeline),
step 2).

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: langchain-oss-automated-triage[bot] <248757908+langchain-oss-automated-triage[bot]@users.noreply.github.com>
Co-authored-by: Johannes du Plessis <johannes@langchain.dev>
Mason Daugherty (mdrxy) added a commit that referenced this pull request Jul 30, 2026
Clicking a copyable value in a modal (for example the thread id in the
Debug Console) no longer fights with text still selected in the
conversation behind it — only the value you clicked lands on the
clipboard.

---

Text selected with the mouse is auto-copied on mouse release, and that
scan walked `App.query("*")`. Textual roots `App.query` at the app's
*default* (compose) screen rather than the active one, so a selection
left behind in the transcript stayed in scope even while a modal was on
top. Any click inside the modal then produced two clipboard writes — the
modal's own copy plus the stale transcript selection — and whichever
landed last won.

Selections are per-screen state, so the scan is now scoped to a single
screen: `copy_selection_to_clipboard` takes an optional keyword-only
`screen` (defaulting to the app's active screen), and the mouse-up
handler pins it to the screen the release actually landed on. Background
selections are left untouched and visible, matching the current
behavior; they simply aren't copied by clicks that happened somewhere
else. As a side effect, selections made *inside* a modal are now
copyable, which the app-rooted scan never reached.

The new regression tests drive a real selection through Textual's event
path (triple-click via `Pilot`) with a modal pushed on top, and fail
against the previous implementation.

Made by [Open
SWE](https://openswe.vercel.app/agents/bde43330-a625-d287-611f-af90f64dac82)

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Mason Daugherty (mdrxy) pushed a commit that referenced this pull request Jul 30, 2026
> [!CAUTION]
> Merging this PR will automatically publish to **PyPI** and create a
**GitHub release**.

For the full release process, see
[`.github/RELEASING.md`](https://github.com/langchain-ai/deepagents/blob/main/.github/RELEASING.md).

---

_Release notes preview: keep this section in sync with the package
`CHANGELOG.md`. Publish reads the merged CHANGELOG via `release.yml`,
not this PR description — keep them aligned anyway so the PR stays an
accurate historical record for reviewers and anyone returning later._

---


##
[0.1.50](deepagents-code==0.1.49...deepagents-code==0.1.50)
(2026-07-30)

### Highlights

- Added project hooks workspace trust and expanded Hooks v2 support with
client and server lifecycle events plus runtime feedback
([#5105](#5105),
[#5104](#5104),
[#4997](#4997),
[#5045](#5045)).
- Added an option to mute the “YOLO is active” toast
([#5103](#5103)).
- Made the splash screen `thread` ID clickable to copy it
([#5173](#5173)).
- Show `ask_user` answers directly on the answered tool row
([#5100](#5100)).
- Show a toast when submitting an empty required `ask_user` answer
([#5095](#5095)).
- Added thread message counts to the Debug Console
([#5117](#5117)).

### Fixes and improvements

- Gated Hooks v2 behind `DEEPAGENTS_CODE_EXPERIMENTAL` and improved hook
resume stability across identity and Command tool results
([#5146](#5146),
[#5176](#5176)).
- Kept server hook state out of task results
([#5164](#5164)).
- Stopped duplicate Auto transcript events during interrupt replay
([#5157](#5157)).
- Kept `/update` and `/install --package` prompts responsive
([#5127](#5127)).
- Refreshed the `/threads` cache after each turn
([#5174](#5174)).
- Anchored toasts above the chat input and added a toast when media is
dropped into a free-text question
([#5101](#5101),
[#5099](#5099)).
- Improved thread status message styling and links
([#5118](#5118)).
- Made resume hints echo the launched command name
([#5119](#5119)).
- Scoped selection copy to the clicked screen
([#5140](#5140)).
- Ignored mouse hits on detached widgets
([#5114](#5114)).

_End release notes preview._

---

> [!NOTE]
> A **New Contributors** section is appended to the GitHub release notes
automatically at publish time (see [Release
Pipeline](https://github.com/langchain-ai/deepagents/blob/main/.github/RELEASING.md#release-pipeline),
step 2).

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: langchain-oss-automated-triage[bot] <248757908+langchain-oss-automated-triage[bot]@users.noreply.github.com>
Co-authored-by: Johannes du Plessis <johannes@langchain.dev>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dcode Related to `deepagents-code` fix A bug fix (PATCH) internal User is a member of the `langchain-ai` GitHub organization size: S 50-199 LOC

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant