webbee 0.3.45__tar.gz → 0.3.47__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.47}/CHANGELOG.md +42 -0
- {webbee-0.3.45 → webbee-0.3.47}/PKG-INFO +1 -1
- {webbee-0.3.45 → webbee-0.3.47}/pyproject.toml +1 -1
- webbee-0.3.47/src/webbee/__init__.py +1 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/coding_context.py +24 -1
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/selection.py +20 -2
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_coding_context.py +12 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_session.py +4 -1
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_tui.py +36 -0
- webbee-0.3.45/src/webbee/__init__.py +0 -1
- {webbee-0.3.45 → webbee-0.3.47}/.github/workflows/publish.yml +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/.gitignore +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/LICENSE +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/README.md +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/install.sh +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/account.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/active_sessions.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/banner_art.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/boot.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/checkpoints.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/cli.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/clipboard.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/clipboard_read.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/commands.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/config.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/consent.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/details.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/events.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/frames.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/home.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/home_view.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/http.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/instance_lock.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/intel/__init__.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/intel/chunker.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/intel/embed.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/intel/graph.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/intel/indexer.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/intel/models.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/intel/query.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/intel/service.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/intel/store.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/intel/vectors.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/intel/watch.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/mode_store.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/newtab_mode.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/output_pane.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/queue_panel.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/reflow.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/remote.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/render.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/repl.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/repo.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/session.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/sessions.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/sizing.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/slots.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/steer.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/stream.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/tab_store.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/tabs.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/thread.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/tier_store.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/todo_panel.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/tokens.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/tools.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/tui.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/update.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/urlopen.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/wallet.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/src/webbee/worktrees.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/__init__.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/conftest.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_account.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_active_sessions.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_audit_2026_07_25_findings.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_checkpoints.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_cli.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_clipboard.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_clipboard_read.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_commands.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_config.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_cpc_contract_stable.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_details.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_events.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_freeze_fix.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_home.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_home_view.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_instance_lock.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_intel_chunker.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_intel_embed.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_intel_graph.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_intel_indexer.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_intel_no_numpy.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_intel_query.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_intel_service.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_intel_store.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_intel_vectors.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_intel_watch.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_marathon.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_mode_store.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_newtab_mode.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_packaging.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_queue_manage_0337.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_reflow.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_render.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_repl.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_repo.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_sessions.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_sizing.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_slots.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_steer.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_stream.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_tab_restore_0337.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_tabs.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_terminal_ux_0336.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_thread.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_tier_store.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_todo_panel.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_tokens.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_tools.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_tui_hardening.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_update.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_update_badge_0340.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_urlopen.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_version.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_version_badge_corner.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_wallet.py +0 -0
- {webbee-0.3.45 → webbee-0.3.47}/tests/test_worktrees.py +0 -0
|
@@ -1,5 +1,47 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.47
|
|
4
|
+
|
|
5
|
+
The cloud brain now sees BOTH your machine's real clock and your configured
|
|
6
|
+
Imperal profile timezone on every turn -- never just one blindly trusted.
|
|
7
|
+
|
|
8
|
+
- **What changed:** every coding-context snapshot now carries `client_now`
|
|
9
|
+
(this machine's actual wall-clock reading, ISO 8601 with UTC offset) and
|
|
10
|
+
`client_tz_label` (the OS/IANA timezone name) alongside the existing
|
|
11
|
+
cwd/git/tree/repo_key. The kernel renders both facts side by side and
|
|
12
|
+
flags a drift explicitly (wrong system clock, a VM in a different zone, a
|
|
13
|
+
stale profile setting) instead of silently trusting either source.
|
|
14
|
+
- **Why:** the brain previously only knew the CONFIGURED profile timezone
|
|
15
|
+
server-side -- it had no way to tell if the machine actually running the
|
|
16
|
+
agent disagreed with that (e.g. a VM parked in UTC, a laptop with a wrong
|
|
17
|
+
system clock). Now it always has ground truth from both ends.
|
|
18
|
+
- Best-effort: any clock-read failure just omits the fields -- never blocks
|
|
19
|
+
a turn.
|
|
20
|
+
|
|
21
|
+
## 0.3.46
|
|
22
|
+
|
|
23
|
+
Fixed a real jitter bug in text selection: dragging to select past the
|
|
24
|
+
bottom (or top) of the visible output no longer makes the pane "jump
|
|
25
|
+
around" instead of scrolling smoothly.
|
|
26
|
+
|
|
27
|
+
- **Root cause:** a real terminal fires MOUSE_MOVE far more often than
|
|
28
|
+
once while the pointer sits at the viewport edge (every pixel of hand
|
|
29
|
+
tremor counts as a new event) -- and EVERY one of those was scrolling
|
|
30
|
+
the pane immediately, at the same time the dock's own ticker
|
|
31
|
+
(`edge_tick`) was ALSO scrolling the same pane every 0.25s to keep the
|
|
32
|
+
drag going while the mouse holds still. Two independent scroll sources
|
|
33
|
+
racing each other on the same viewport is exactly what looked like
|
|
34
|
+
"the window runs back and forth" while trying to select a big chunk of
|
|
35
|
+
text (Valentin, live 2026-07-31).
|
|
36
|
+
- **Fix:** the immediate scroll now fires ONLY on the actual edge
|
|
37
|
+
transition (the moment the drag first reaches the edge); every
|
|
38
|
+
following MOUSE_MOVE at the same edge just keeps the selection armed
|
|
39
|
+
and leaves ALL the ongoing scrolling to the ticker's one steady 0.25s
|
|
40
|
+
cadence -- the exact same one-source-of-truth contract the queue/todo
|
|
41
|
+
panels and tab bar already use for their own edge-forwarded drags, now
|
|
42
|
+
applied consistently everywhere in the terminal so no two panes/panels
|
|
43
|
+
ever fight over the same scroll.
|
|
44
|
+
|
|
3
45
|
## 0.3.45
|
|
4
46
|
|
|
5
47
|
Consent prompts stop being an easy-to-miss detail:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.3.47"
|
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
"""Coding-context snapshot for the cloud brain (extracted verbatim from
|
|
2
2
|
webbee.session): build_coding_context packages cwd/git/tree/repo identity
|
|
3
3
|
(+ optional repo_profile), and detect_verify_cmd is the CLIENT-detected
|
|
4
|
-
proof-of-done command a marathon carries in that context.
|
|
4
|
+
proof-of-done command a marathon carries in that context.
|
|
5
|
+
|
|
6
|
+
client_now (2026-07-31, I-CODING-TIME-CONTRACT): the machine's OWN wall-clock
|
|
7
|
+
reading, captured fresh on every snapshot -- distinct from whatever timezone
|
|
8
|
+
the user has CONFIGURED in their Imperal profile/panel. The brain needs BOTH:
|
|
9
|
+
the configured timezone (what the user SET) and the actual reading on the
|
|
10
|
+
machine running the agent (what time it REALLY is there) so it can flag a
|
|
11
|
+
drift (wrong system clock, VM in a different zone, stale profile setting)
|
|
12
|
+
instead of silently trusting one source. Best-effort: any clock read failure
|
|
13
|
+
degrades to an absent key, never blocks the turn."""
|
|
5
14
|
import os
|
|
6
15
|
import subprocess
|
|
16
|
+
from datetime import datetime
|
|
7
17
|
|
|
8
18
|
# Heavy dependency/build dirs the file-tree walk and the agent `grep` tool
|
|
9
19
|
# must NEVER descend: they blow up walk time on real repos and (with the tree's
|
|
@@ -46,6 +56,19 @@ def build_coding_context(workspace_root: str, intel=None) -> dict:
|
|
|
46
56
|
root = find_repo_root(cwd)
|
|
47
57
|
d = {"cwd": cwd, "git": git, "tree": "\n".join(paths),
|
|
48
58
|
"repo_key": compute_repo_key(root), "repo_root": root}
|
|
59
|
+
# I-CODING-TIME-CONTRACT: the machine's actual local wall-clock, ISO 8601
|
|
60
|
+
# WITH UTC offset (so the kernel can compare it against the user's
|
|
61
|
+
# CONFIGURED timezone without re-resolving anything) + the IANA zone name
|
|
62
|
+
# the OS reports, when available. Never raises -- a clock/tzdata error
|
|
63
|
+
# just omits the key, the kernel falls back to the configured-only view.
|
|
64
|
+
try:
|
|
65
|
+
_now_local = datetime.now().astimezone()
|
|
66
|
+
d["client_now"] = _now_local.isoformat(timespec="seconds")
|
|
67
|
+
_tzname = _now_local.tzname()
|
|
68
|
+
if _tzname:
|
|
69
|
+
d["client_tz_label"] = _tzname
|
|
70
|
+
except Exception:
|
|
71
|
+
pass
|
|
49
72
|
if intel is not None and getattr(intel, "ready", False):
|
|
50
73
|
try:
|
|
51
74
|
d["repo_profile"] = intel.repo_profile()
|
|
@@ -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
|
|
@@ -29,3 +29,15 @@ def test_build_coding_context_still_prunes_dotdirs(tmp_path):
|
|
|
29
29
|
tree = build_coding_context(str(tmp_path))["tree"]
|
|
30
30
|
assert "src/a.py" in tree
|
|
31
31
|
assert ".secret" not in tree
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_build_coding_context_carries_client_now(tmp_path):
|
|
35
|
+
# I-CODING-TIME-CONTRACT: the brain needs the machine's ACTUAL wall-clock
|
|
36
|
+
# reading (distinct from the user's configured profile timezone) on every
|
|
37
|
+
# snapshot, so it can flag drift instead of blindly trusting one source.
|
|
38
|
+
import re
|
|
39
|
+
ctx = build_coding_context(str(tmp_path))
|
|
40
|
+
assert "client_now" in ctx
|
|
41
|
+
# ISO 8601 with an explicit UTC offset (+HH:MM/-HH:MM or Z) -- the kernel
|
|
42
|
+
# compares this against the configured timezone without re-resolving.
|
|
43
|
+
assert re.match(r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{2}:\d{2}$", ctx["client_now"])
|
|
@@ -51,7 +51,10 @@ def test_confirm_default_relays_raw_reply_verbatim():
|
|
|
51
51
|
def test_build_coding_context_shape(tmp_path):
|
|
52
52
|
(tmp_path / "a.txt").write_text("x")
|
|
53
53
|
ctx = build_coding_context(str(tmp_path))
|
|
54
|
-
|
|
54
|
+
# I-CODING-TIME-CONTRACT: client_now/client_tz_label ride every snapshot
|
|
55
|
+
# (best-effort -- a clock-read failure would simply omit them).
|
|
56
|
+
assert set(ctx) == {"cwd", "git", "tree", "repo_key", "repo_root",
|
|
57
|
+
"client_now", "client_tz_label"}
|
|
55
58
|
assert "a.txt" in ctx["tree"]
|
|
56
59
|
|
|
57
60
|
|
|
@@ -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
|