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.
Files changed (129) hide show
  1. {webbee-0.3.45 → webbee-0.3.46}/CHANGELOG.md +24 -0
  2. {webbee-0.3.45 → webbee-0.3.46}/PKG-INFO +1 -1
  3. {webbee-0.3.45 → webbee-0.3.46}/pyproject.toml +1 -1
  4. webbee-0.3.46/src/webbee/__init__.py +1 -0
  5. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/selection.py +20 -2
  6. {webbee-0.3.45 → webbee-0.3.46}/tests/test_tui.py +36 -0
  7. webbee-0.3.45/src/webbee/__init__.py +0 -1
  8. {webbee-0.3.45 → webbee-0.3.46}/.github/workflows/publish.yml +0 -0
  9. {webbee-0.3.45 → webbee-0.3.46}/.gitignore +0 -0
  10. {webbee-0.3.45 → webbee-0.3.46}/LICENSE +0 -0
  11. {webbee-0.3.45 → webbee-0.3.46}/README.md +0 -0
  12. {webbee-0.3.45 → webbee-0.3.46}/install.sh +0 -0
  13. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/account.py +0 -0
  14. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/active_sessions.py +0 -0
  15. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/banner_art.py +0 -0
  16. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/boot.py +0 -0
  17. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/checkpoints.py +0 -0
  18. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/cli.py +0 -0
  19. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/clipboard.py +0 -0
  20. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/clipboard_read.py +0 -0
  21. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/coding_context.py +0 -0
  22. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/commands.py +0 -0
  23. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/config.py +0 -0
  24. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/consent.py +0 -0
  25. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/details.py +0 -0
  26. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/events.py +0 -0
  27. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/frames.py +0 -0
  28. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/home.py +0 -0
  29. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/home_view.py +0 -0
  30. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/http.py +0 -0
  31. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/instance_lock.py +0 -0
  32. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/intel/__init__.py +0 -0
  33. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/intel/chunker.py +0 -0
  34. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/intel/embed.py +0 -0
  35. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/intel/graph.py +0 -0
  36. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/intel/indexer.py +0 -0
  37. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/intel/models.py +0 -0
  38. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/intel/query.py +0 -0
  39. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/intel/service.py +0 -0
  40. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/intel/store.py +0 -0
  41. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/intel/vectors.py +0 -0
  42. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/intel/watch.py +0 -0
  43. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/mode_store.py +0 -0
  44. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/newtab_mode.py +0 -0
  45. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/output_pane.py +0 -0
  46. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/queue_panel.py +0 -0
  47. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/reflow.py +0 -0
  48. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/remote.py +0 -0
  49. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/render.py +0 -0
  50. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/repl.py +0 -0
  51. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/repo.py +0 -0
  52. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/session.py +0 -0
  53. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/sessions.py +0 -0
  54. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/sizing.py +0 -0
  55. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/slots.py +0 -0
  56. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/steer.py +0 -0
  57. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/stream.py +0 -0
  58. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/tab_store.py +0 -0
  59. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/tabs.py +0 -0
  60. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/thread.py +0 -0
  61. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/tier_store.py +0 -0
  62. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/todo_panel.py +0 -0
  63. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/tokens.py +0 -0
  64. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/tools.py +0 -0
  65. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/tui.py +0 -0
  66. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/update.py +0 -0
  67. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/urlopen.py +0 -0
  68. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/wallet.py +0 -0
  69. {webbee-0.3.45 → webbee-0.3.46}/src/webbee/worktrees.py +0 -0
  70. {webbee-0.3.45 → webbee-0.3.46}/tests/__init__.py +0 -0
  71. {webbee-0.3.45 → webbee-0.3.46}/tests/conftest.py +0 -0
  72. {webbee-0.3.45 → webbee-0.3.46}/tests/test_account.py +0 -0
  73. {webbee-0.3.45 → webbee-0.3.46}/tests/test_active_sessions.py +0 -0
  74. {webbee-0.3.45 → webbee-0.3.46}/tests/test_audit_2026_07_25_findings.py +0 -0
  75. {webbee-0.3.45 → webbee-0.3.46}/tests/test_checkpoints.py +0 -0
  76. {webbee-0.3.45 → webbee-0.3.46}/tests/test_cli.py +0 -0
  77. {webbee-0.3.45 → webbee-0.3.46}/tests/test_clipboard.py +0 -0
  78. {webbee-0.3.45 → webbee-0.3.46}/tests/test_clipboard_read.py +0 -0
  79. {webbee-0.3.45 → webbee-0.3.46}/tests/test_coding_context.py +0 -0
  80. {webbee-0.3.45 → webbee-0.3.46}/tests/test_commands.py +0 -0
  81. {webbee-0.3.45 → webbee-0.3.46}/tests/test_config.py +0 -0
  82. {webbee-0.3.45 → webbee-0.3.46}/tests/test_cpc_contract_stable.py +0 -0
  83. {webbee-0.3.45 → webbee-0.3.46}/tests/test_details.py +0 -0
  84. {webbee-0.3.45 → webbee-0.3.46}/tests/test_events.py +0 -0
  85. {webbee-0.3.45 → webbee-0.3.46}/tests/test_freeze_fix.py +0 -0
  86. {webbee-0.3.45 → webbee-0.3.46}/tests/test_home.py +0 -0
  87. {webbee-0.3.45 → webbee-0.3.46}/tests/test_home_view.py +0 -0
  88. {webbee-0.3.45 → webbee-0.3.46}/tests/test_instance_lock.py +0 -0
  89. {webbee-0.3.45 → webbee-0.3.46}/tests/test_intel_chunker.py +0 -0
  90. {webbee-0.3.45 → webbee-0.3.46}/tests/test_intel_embed.py +0 -0
  91. {webbee-0.3.45 → webbee-0.3.46}/tests/test_intel_graph.py +0 -0
  92. {webbee-0.3.45 → webbee-0.3.46}/tests/test_intel_indexer.py +0 -0
  93. {webbee-0.3.45 → webbee-0.3.46}/tests/test_intel_no_numpy.py +0 -0
  94. {webbee-0.3.45 → webbee-0.3.46}/tests/test_intel_query.py +0 -0
  95. {webbee-0.3.45 → webbee-0.3.46}/tests/test_intel_service.py +0 -0
  96. {webbee-0.3.45 → webbee-0.3.46}/tests/test_intel_store.py +0 -0
  97. {webbee-0.3.45 → webbee-0.3.46}/tests/test_intel_vectors.py +0 -0
  98. {webbee-0.3.45 → webbee-0.3.46}/tests/test_intel_watch.py +0 -0
  99. {webbee-0.3.45 → webbee-0.3.46}/tests/test_marathon.py +0 -0
  100. {webbee-0.3.45 → webbee-0.3.46}/tests/test_mode_store.py +0 -0
  101. {webbee-0.3.45 → webbee-0.3.46}/tests/test_newtab_mode.py +0 -0
  102. {webbee-0.3.45 → webbee-0.3.46}/tests/test_packaging.py +0 -0
  103. {webbee-0.3.45 → webbee-0.3.46}/tests/test_queue_manage_0337.py +0 -0
  104. {webbee-0.3.45 → webbee-0.3.46}/tests/test_reflow.py +0 -0
  105. {webbee-0.3.45 → webbee-0.3.46}/tests/test_render.py +0 -0
  106. {webbee-0.3.45 → webbee-0.3.46}/tests/test_repl.py +0 -0
  107. {webbee-0.3.45 → webbee-0.3.46}/tests/test_repo.py +0 -0
  108. {webbee-0.3.45 → webbee-0.3.46}/tests/test_session.py +0 -0
  109. {webbee-0.3.45 → webbee-0.3.46}/tests/test_sessions.py +0 -0
  110. {webbee-0.3.45 → webbee-0.3.46}/tests/test_sizing.py +0 -0
  111. {webbee-0.3.45 → webbee-0.3.46}/tests/test_slots.py +0 -0
  112. {webbee-0.3.45 → webbee-0.3.46}/tests/test_steer.py +0 -0
  113. {webbee-0.3.45 → webbee-0.3.46}/tests/test_stream.py +0 -0
  114. {webbee-0.3.45 → webbee-0.3.46}/tests/test_tab_restore_0337.py +0 -0
  115. {webbee-0.3.45 → webbee-0.3.46}/tests/test_tabs.py +0 -0
  116. {webbee-0.3.45 → webbee-0.3.46}/tests/test_terminal_ux_0336.py +0 -0
  117. {webbee-0.3.45 → webbee-0.3.46}/tests/test_thread.py +0 -0
  118. {webbee-0.3.45 → webbee-0.3.46}/tests/test_tier_store.py +0 -0
  119. {webbee-0.3.45 → webbee-0.3.46}/tests/test_todo_panel.py +0 -0
  120. {webbee-0.3.45 → webbee-0.3.46}/tests/test_tokens.py +0 -0
  121. {webbee-0.3.45 → webbee-0.3.46}/tests/test_tools.py +0 -0
  122. {webbee-0.3.45 → webbee-0.3.46}/tests/test_tui_hardening.py +0 -0
  123. {webbee-0.3.45 → webbee-0.3.46}/tests/test_update.py +0 -0
  124. {webbee-0.3.45 → webbee-0.3.46}/tests/test_update_badge_0340.py +0 -0
  125. {webbee-0.3.45 → webbee-0.3.46}/tests/test_urlopen.py +0 -0
  126. {webbee-0.3.45 → webbee-0.3.46}/tests/test_version.py +0 -0
  127. {webbee-0.3.45 → webbee-0.3.46}/tests/test_version_badge_corner.py +0 -0
  128. {webbee-0.3.45 → webbee-0.3.46}/tests/test_wallet.py +0 -0
  129. {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:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: webbee
3
- Version: 0.3.45
3
+ Version: 0.3.46
4
4
  Summary: Webbee 🐝 — the Imperal Cloud coding agent in your terminal
5
5
  Project-URL: Homepage, https://imperal.io
6
6
  Project-URL: Documentation, https://docs.imperal.io
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "webbee"
3
- version = "0.3.45"
3
+ version = "0.3.46"
4
4
  description = "Webbee 🐝 — the Imperal Cloud coding agent in your terminal"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -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.scroll(3)
107
+ if pane._edge_drag != 1:
108
+ pane.scroll(3)
92
109
  pane._edge_drag = 1
93
110
  elif y <= 0:
94
- pane.scroll(-3)
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