Keep a selection in a bubble; typing or deselecting gives the caret back #11
Closed
asxpi
wants to merge 0 commits from
asxpi/anotherim-js:fix/bubble-selection-caret into develop
pull from: asxpi/anotherim-js:fix/bubble-selection-caret
merge into: another-im:develop
another-im:develop
another-im:fix/relock-dismiss
another-im:fix/settings-accounts
another-im:feat/unified-dialogs-1
another-im:feat/unified-dialogs-2
another-im:feat/unified-dialogs-3
another-im:feat/one-status
another-im:macos-0.6.1
another-im:fix/console-tab-out
another-im:fix/desktop-drop
another-im:fix/xml-console-menu
another-im:feat/telegram-connect
another-im:feat/chat-tags
another-im:feat/muc-omemo
another-im:integrate/origin-develop-2026-10-02
another-im:fix/media-bubble-size
another-im:fix/empty-click-focus
another-im:feat/system-font-default
another-im:integrate/develop-2026-10-02
another-im:fix/attachment-decrypt
another-im:feat/system-font
another-im:fix/chat-links
another-im:fix/android-background-network
another-im:feat/tray
another-im:feat/thumb-cache
another-im:fix/android-csi-native
another-im:feat/opfs-store
another-im:fix/alttab-focus
another-im:try/groupcalls
another-im:feat/pwa-install
another-im:integrate/0.3.0
another-im:fix/upload-bubble-030
another-im:feat/drag-select
another-im:fix/upload-bubble
another-im:feat/encryption-info
another-im:chore/libtacky-path
another-im:feat/chat-header-menus
another-im:integrate/develop
another-im:fix/saved-top-linux-voice
another-im:fix/dialog-close-icons
another-im:feat/window-title-unread
another-im:feat/groupcalls
another-im:fix/open-picture-one-tap
another-im:feat/server-info-console
another-im:fix/chat-open-scroll-end
another-im:fix/desktop-push-mutes
another-im:fix/android-mute-push-selection
another-im:feat/otr-lock-colours
another-im:otr-v3-secret-chats
another-im:chore/remove-otr
another-im:feat/room-invites
another-im:feat/voice-video-messages
another-im:docs/readme
another-im:fix/android-empty-files
another-im:api-freeze
another-im:feat/push-counts
another-im:feat/h264-android-save
another-im:feat/keep-attachments-decrypted
another-im:feat/media-player
another-im:feat/fix-subdomain-attachments
another-im:feat/fonts-pt-sans
another-im:feat/always-decrypt-on-the-fly
another-im:feat/default-scale-125
another-im:fix/composer-focus-after-send
another-im:feat/android-native-calls
another-im:feat/media-stream-decrypt
another-im:feat/download-failure
another-im:feat/outbox-cap
another-im:feat/web-call-sheet
another-im:feat/first-run-push-check
another-im:feat/font-size-110
another-im:feat/log-view
another-im:fix/failed-transfer-history
another-im:feat/secure-unlock
another-im:feat/tpm-desktop
another-im:feat/tpm-android
another-im:feat/api-freeze-client
No reviewers
Labels
Clear labels
bug
Something is not working
duplicate
This issue or pull request already exists
feature request
New feature
linux
linux exclusive
macos
macos exclusive
needsinfo
Needs more information
severity: high
Major issue, significantly impacts usability or blocks core functionality
severity: low
Minor issue, cosmetic or rarely affects usage
severity: medium
Noticeable issue, but there is a workaround or limited impact
telegram_gw
issues related to the telegram gw
ui/ux
issues related to the interface and user experience (visual glitches, layout, interactions, usability problems)
windows
windows exclusive
wontfix
This won't be fixed
No labels
bug
duplicate
feature request
linux
macos
needsinfo
severity: high
severity: low
severity: medium
telegram_gw
ui/ux
windows
wontfix
Milestone
Clear milestone
No items
No milestone
Projects
Clear projects
No items
No project
Assignees
Clear assignees
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".
No due date set.
Dependencies
No dependencies set
Reference
another-im/anotherim-js!11
Loading…
Reference in a new issue
No description provided.
Delete branch "asxpi/anotherim-js:fix/bubble-selection-caret"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
On the Linux desktop app (WebKitGTK), words selected in a message disappeared as soon as the mouse was released, and the caret jumped back to the composer.
Cause.
#refocus(a click in the feed gives the caret back) checked for a selection withdocument.getSelection(). A message's words live in its row's shadow root, and WebKit reports a selection there todocument.getSelection()as collapsed, empty and with no range. Measured in WebKitGTK on the mock:isCollapsed: true,"",rangeCount: 0. So the click that ends a drag saw nothing selected and focused the composer. Blink reports it differently, which is likely why it wasn't noticed.1. Keep the selection (
5612014)hasTextSelection()inui/src/views/text-selection.ts, which also asksSelection.getComposedRanges().empty-space.ts(keeping the native menu over selected text) had the same check and now uses the helper.2. Getting back to the composer (
06c259f). With the selection kept, the focus stays off the composer, so:#takeCaretruns. Not mid-press, where a new sweep starts: the press's release decides, and only if nothing is selected by then.composer-focus.test.tsasserted that other letters were left alone. It's updated to the new rule.Tests.
ui/test/text-selection.test.tshas 14 cases: the helper, a feed click with a caret and with a selection, typing, and deselecting. The stand-in Selection behaves like WebKit, rescoping to the host when no roots are given. The plain-click case and the shadow-selection case fail on the old code.tscpasses.make test-ui: 2162 passed, 2 failed, both the thread-footer cases, which fail under load and pass alone.Checked by hand in a local Flatpak build of 0.5.2:
🤖 Generated with Claude Code
Added
a479fa1(composer: Tab types a tab; no scrollbar under the empty field). Both came up while testing this branch.Tab in the message field. Tab moved the focus on to the mic or Send. It now inserts a tab at the caret, replacing any selected words, through
insertText, so Ctrl+Z removes it. Shift+Tab still leaves the field, so the keyboard is never trapped there, and Ctrl+Tab still goes to the next chat.Horizontal scrollbar under the empty field (WebKitGTK). At an interface scale of 125% and up (CSS
zoom), WebKitGTK lays out a textarea's placeholder wider than the box. That happens even for the emptyplaceholder=""the chat's field sets: 531px of scroll in a 430px field at 125%. The bar went away at the first key. The attribute is now left off when there are no words, and the textarea getsoverflow-x: hiddenfor the composers that do have a placeholder (the thread's). Measured afterwards in WebKitGTK: 430/430 at 125%. At 150% only a 1px rounding is left, whichoverflow-x: hiddenkeeps from drawing a bar.Checked:
composer-focus.test.ts: 5 new cases, 3 for Tab and 2 for the placeholder attribute. Where the caret lands after the tab isn't asserted there: happy-dom has noexecCommand, and itssetRangeText(..., 'end')puts the caret at the end of the whole value.if x:|done), the focus stays in the field, andundotakes the tab back.tscpasses.make test-ui: 2166 passed. The 3 failures are the thread-footer cases andchat-view's "names a room reply"; each has failed in some full runs and passed alone.Merged into develop by hand with a merge commit and released in 0.5.3 (v0.5.3 =
87ebd52b, https://dev.another.im/another-im/anotherim-js/releases/tag/v0.5.3). The forge does not mark a hand merge as merged, so this PR is closed here. One follow-up on top: the chat reads the selection once per press rather than per selectionchange event (d231e462).Pull request closed