webbee 0.3.45__tar.gz → 0.3.46__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {webbee-0.3.45 → webbee-0.3.46}/CHANGELOG.md +24 -0
- {webbee-0.3.45 → webbee-0.3.46}/PKG-INFO +1 -1
- {webbee-0.3.45 → webbee-0.3.46}/pyproject.toml +1 -1
- webbee-0.3.46/src/webbee/__init__.py +1 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/selection.py +20 -2
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_tui.py +36 -0
- webbee-0.3.45/src/webbee/__init__.py +0 -1
- {webbee-0.3.45 → webbee-0.3.46}/.github/workflows/publish.yml +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/.gitignore +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/LICENSE +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/README.md +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/install.sh +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/account.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/active_sessions.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/banner_art.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/boot.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/checkpoints.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/cli.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/clipboard.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/clipboard_read.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/coding_context.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/commands.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/config.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/consent.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/details.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/events.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/frames.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/home.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/home_view.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/http.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/instance_lock.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/intel/__init__.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/intel/chunker.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/intel/embed.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/intel/graph.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/intel/indexer.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/intel/models.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/intel/query.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/intel/service.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/intel/store.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/intel/vectors.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/intel/watch.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/mode_store.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/newtab_mode.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/output_pane.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/queue_panel.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/reflow.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/remote.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/render.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/repl.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/repo.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/session.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/sessions.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/sizing.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/slots.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/steer.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/stream.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/tab_store.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/tabs.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/thread.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/tier_store.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/todo_panel.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/tokens.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/tools.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/tui.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/update.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/urlopen.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/wallet.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/src/webbee/worktrees.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/__init__.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/conftest.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_account.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_active_sessions.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_audit_2026_07_25_findings.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_checkpoints.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_cli.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_clipboard.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_clipboard_read.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_coding_context.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_commands.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_config.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_cpc_contract_stable.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_details.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_events.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_freeze_fix.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_home.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_home_view.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_instance_lock.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_intel_chunker.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_intel_embed.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_intel_graph.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_intel_indexer.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_intel_no_numpy.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_intel_query.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_intel_service.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_intel_store.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_intel_vectors.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_intel_watch.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_marathon.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_mode_store.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_newtab_mode.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_packaging.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_queue_manage_0337.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_reflow.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_render.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_repl.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_repo.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_session.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_sessions.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_sizing.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_slots.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_steer.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_stream.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_tab_restore_0337.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_tabs.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_terminal_ux_0336.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_thread.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_tier_store.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_todo_panel.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_tokens.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_tools.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_tui_hardening.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_update.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_update_badge_0340.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_urlopen.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_version.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_version_badge_corner.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_wallet.py +0 -0
- {webbee-0.3.45 → webbee-0.3.46}/tests/test_worktrees.py +0 -0
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.46
|
|
4
|
+
|
|
5
|
+
Fixed a real jitter bug in text selection: dragging to select past the
|
|
6
|
+
bottom (or top) of the visible output no longer makes the pane "jump
|
|
7
|
+
around" instead of scrolling smoothly.
|
|
8
|
+
|
|
9
|
+
- **Root cause:** a real terminal fires MOUSE_MOVE far more often than
|
|
10
|
+
once while the pointer sits at the viewport edge (every pixel of hand
|
|
11
|
+
tremor counts as a new event) -- and EVERY one of those was scrolling
|
|
12
|
+
the pane immediately, at the same time the dock's own ticker
|
|
13
|
+
(`edge_tick`) was ALSO scrolling the same pane every 0.25s to keep the
|
|
14
|
+
drag going while the mouse holds still. Two independent scroll sources
|
|
15
|
+
racing each other on the same viewport is exactly what looked like
|
|
16
|
+
"the window runs back and forth" while trying to select a big chunk of
|
|
17
|
+
text (Valentin, live 2026-07-31).
|
|
18
|
+
- **Fix:** the immediate scroll now fires ONLY on the actual edge
|
|
19
|
+
transition (the moment the drag first reaches the edge); every
|
|
20
|
+
following MOUSE_MOVE at the same edge just keeps the selection armed
|
|
21
|
+
and leaves ALL the ongoing scrolling to the ticker's one steady 0.25s
|
|
22
|
+
cadence -- the exact same one-source-of-truth contract the queue/todo
|
|
23
|
+
panels and tab bar already use for their own edge-forwarded drags, now
|
|
24
|
+
applied consistently everywhere in the terminal so no two panes/panels
|
|
25
|
+
ever fight over the same scroll.
|
|
26
|
+
|
|
3
27
|
## 0.3.45
|
|
4
28
|
|
|
5
29
|
Consent prompts stop being an easy-to-miss detail:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.3.46"
|
|
@@ -87,11 +87,29 @@ def make_select_control(pane, FormattedTextControl, MouseEventType, MouseButton)
|
|
|
87
87
|
return NotImplemented
|
|
88
88
|
pane._edge_ticks = 0 # a fresh MOUSE_MOVE means the pointer isn't parked
|
|
89
89
|
y = ev.position.y
|
|
90
|
+
# webbee-code-selection-scroll-jitter-v1: a REAL terminal fires
|
|
91
|
+
# MOUSE_MOVE far more often than once (every pixel of hand
|
|
92
|
+
# tremor while parked at the edge trying to select further) --
|
|
93
|
+
# scrolling immediately on EVERY one of those, on top of
|
|
94
|
+
# edge_tick() ALREADY scrolling the same pane every 0.25s once
|
|
95
|
+
# armed, stacked two independent scroll sources racing each
|
|
96
|
+
# other and made the viewport visibly jump/jitter instead of
|
|
97
|
+
# scrolling smoothly (Valentin, live 2026-07-31: "окно бегает
|
|
98
|
+
# туда сюда"). The immediate nudge now fires ONLY on the edge
|
|
99
|
+
# transition (armed 0 -> 1/-1) for snappy first-touch
|
|
100
|
+
# feedback; every following MOUSE_MOVE that's STILL at the
|
|
101
|
+
# same edge just re-affirms the arm (resets _edge_ticks above,
|
|
102
|
+
# so the runaway guard doesn't fire) and leaves the ONGOING
|
|
103
|
+
# scroll entirely to edge_tick()'s single steady cadence --
|
|
104
|
+
# exactly the contract forward_mouse() already uses for the
|
|
105
|
+
# neighbor panels.
|
|
90
106
|
if y >= pane._view_h - 1:
|
|
91
|
-
pane.
|
|
107
|
+
if pane._edge_drag != 1:
|
|
108
|
+
pane.scroll(3)
|
|
92
109
|
pane._edge_drag = 1
|
|
93
110
|
elif y <= 0:
|
|
94
|
-
pane.
|
|
111
|
+
if pane._edge_drag != -1:
|
|
112
|
+
pane.scroll(-3)
|
|
95
113
|
pane._edge_drag = -1
|
|
96
114
|
else:
|
|
97
115
|
pane._edge_drag = 0
|
|
@@ -258,6 +258,42 @@ def test_drag_at_bottom_edge_scrolls_and_grows_selection():
|
|
|
258
258
|
assert pane._edge_drag == 0
|
|
259
259
|
|
|
260
260
|
|
|
261
|
+
def test_repeated_mouse_move_at_same_edge_does_not_double_scroll():
|
|
262
|
+
# webbee-code-selection-scroll-jitter-v1: a real terminal fires MOUSE_MOVE
|
|
263
|
+
# repeatedly while the pointer sits at the SAME edge (hand tremor) --
|
|
264
|
+
# only the FIRST one (the 0 -> armed transition) may scroll immediately;
|
|
265
|
+
# every following MOUSE_MOVE at the same edge must leave the offset
|
|
266
|
+
# alone (still armed, still selecting) and let edge_tick() do the only
|
|
267
|
+
# ongoing scrolling, one steady source instead of two racing.
|
|
268
|
+
from prompt_toolkit.data_structures import Point
|
|
269
|
+
from prompt_toolkit.mouse_events import MouseButton, MouseEvent, MouseEventType
|
|
270
|
+
|
|
271
|
+
from webbee.tui import OutputPane
|
|
272
|
+
|
|
273
|
+
pane = OutputPane(width=80)
|
|
274
|
+
pane._view_h = 10
|
|
275
|
+
pane._io.write("\n".join(f"line{i}" for i in range(100)))
|
|
276
|
+
pane._offset = 0
|
|
277
|
+
|
|
278
|
+
down = MouseEvent(position=Point(0, 5), event_type=MouseEventType.MOUSE_DOWN,
|
|
279
|
+
button=MouseButton.LEFT, modifiers=frozenset())
|
|
280
|
+
pane.control.mouse_handler(down)
|
|
281
|
+
|
|
282
|
+
move = MouseEvent(position=Point(3, 9), event_type=MouseEventType.MOUSE_MOVE,
|
|
283
|
+
button=MouseButton.LEFT, modifiers=frozenset())
|
|
284
|
+
pane.control.mouse_handler(move)
|
|
285
|
+
assert pane._offset == 3 and pane._edge_drag == 1 # first touch: one immediate nudge
|
|
286
|
+
|
|
287
|
+
# the pointer trembles but stays at the SAME bottom row -- must NOT scroll again.
|
|
288
|
+
pane.control.mouse_handler(move)
|
|
289
|
+
pane.control.mouse_handler(move)
|
|
290
|
+
assert pane._offset == 3 and pane._edge_drag == 1 # unchanged -- no double scroll
|
|
291
|
+
|
|
292
|
+
# only edge_tick() advances it further, at its own steady cadence.
|
|
293
|
+
pane.edge_tick()
|
|
294
|
+
assert pane._offset == 6
|
|
295
|
+
|
|
296
|
+
|
|
261
297
|
def test_edge_drag_resets_on_mouse_up_and_top_edge_mirrors():
|
|
262
298
|
from prompt_toolkit.data_structures import Point
|
|
263
299
|
from prompt_toolkit.mouse_events import MouseButton, MouseEvent, MouseEventType
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.3.45"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|