Files
windmill/frontend
AlexRV12andClaude Opus 5 5da4ea43fb feat: show the new-tab icon on a chat path pill while the modifier is held (#10976)
* feat: show the new-tab icon on a chat path pill while the modifier is held

Fixes WIN-2477

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LAa9nNcYxDN3qAZPrYg4Lq

* fix: read the new-tab modifier in the capture phase

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LAa9nNcYxDN3qAZPrYg4Lq

* refactor: track the new-tab modifier only while a pill is hovered

The window key listeners were installed at import time and never removed, so
every page that loaded the module paid for them whether or not a pill existed.
They now attach on mouseenter and detach on mouseleave or destroy, which is the
only window in which the answer is read.

Seeding the flag from the hover event also removes the limitation the previous
version documented: a mouse event carries the same modifier flags as a key
event, so a modifier held before the pointer arrived, or while this window was
unfocused, now reads correctly.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LAa9nNcYxDN3qAZPrYg4Lq

* refactor: export the new-tab modifier as a read-only view

`newTabModifier` handed every consumer a writable handle on module-global
state, so any of them could drive the icon of every pill on the page. The
getter form is what frontend/AGENTS.md prescribes for shared reactive state.

Tearing each attachment down in the test's afterEach as well: the module state
and its window listeners outlive the DOM, so emptying the body left `held` and
the hovered node set for the following case.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LAa9nNcYxDN3qAZPrYg4Lq

* refactor: only track the modifier for pills whose icon can change

The attachment went on every path pill, so hovering a drawer or plain-link pill
installed three window listeners for a flag its icon never reads. Only a
preview pill can flip, so only it gets them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LAa9nNcYxDN3qAZPrYg4Lq

* fix: re-read the new-tab modifier from pointer movement

A modifier held across a keyboard app switch was cleared by the blur and never
restored: the key was down the whole time so no keydown arrived on the way
back, and the pointer parked on the pill fired no fresh mouseenter either. The
pill then showed the panel icon while the click would have opened a tab.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LAa9nNcYxDN3qAZPrYg4Lq

* refactor: give each pill its own modifier state

The shared module state forced a node-identity guard: one hovered element owned
the window listeners, so a pill destroyed elsewhere in the transcript had to be
stopped from tearing them down. A factory per pill removes the guard, its test
case, and the whole class of cross-instance interference, and narrows re-renders
to the hovered pill instead of every preview pill on screen.

Listener teardown now goes through AbortController signals, so leaving a pill
drops the whole set at once rather than through a remove list that has to mirror
every option exactly.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LAa9nNcYxDN3qAZPrYg4Lq

* fix: abort the previous hover controller on re-entry

A second mouseenter with no mouseleave between replaced the controller without
aborting it, so the four listeners registered under the first signal outlived
even the element's destruction: neither leave nor the destroy path held a
reference to reach them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LAa9nNcYxDN3qAZPrYg4Lq

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 16:22:09 +02:00
..

Windmill frontend

The Windmill frontend written in Svelte 5 + Tailwind CSS

The frontend is under AGPL, see the LICENSE file at the root of this repo