railmux 0.3.6.dev2__tar.gz → 0.3.6.dev3__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.
- {railmux-0.3.6.dev2/src/railmux.egg-info → railmux-0.3.6.dev3}/PKG-INFO +1 -1
- railmux-0.3.6.dev3/src/railmux/__init__.py +1 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/fast_display_history.py +39 -52
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3/src/railmux.egg-info}/PKG-INFO +1 -1
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_fast_full_window.py +38 -15
- railmux-0.3.6.dev2/src/railmux/__init__.py +0 -1
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/LICENSE +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/MANIFEST.in +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/README.md +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/pyproject.toml +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/setup.cfg +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/__main__.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/atomic_file.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/background_index.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/cli.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/codex_index.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/config.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/config_cli.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/diagnostics.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/discovery.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/display_transport.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/fast_display_client.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/fast_display_input.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/fast_display_protocol.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/fast_display_server.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/favorites.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/fuzzy.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/help_workspace.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/launcher.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/legacy_sessions.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/local_clipboard.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/local_open.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/models.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/modes.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/mouse_manager.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/orphan_marker.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/pane_surface.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/path_codec.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/remote_config.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/renames.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/restart_state.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/runtime_config.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/scroll_agent.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/scroll_manager.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/selection_isolation.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/self_update.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/session_cache.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/session_index.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/session_lease.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/setting_contracts.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/settings.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/ssh_args.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/ssh_compat.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/ssh_display_diagnostics.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/ssh_doctor.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/system_deps.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/terminal_status.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/tmux_binding_manager.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/tmux_ctl.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/tmux_health.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/tmux_server.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/tool_panes.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/transcript.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/ui/__init__.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/ui/_widgets.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/ui/app.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/ui/keymap.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/ui/modals.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/ui/projects_pane.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/ui/running_pane.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/ui/sessions_pane.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/ui/sidebar.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/ui/statusbar.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux/ui/workspace.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux.egg-info/SOURCES.txt +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux.egg-info/dependency_links.txt +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux.egg-info/entry_points.txt +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux.egg-info/requires.txt +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/src/railmux.egg-info/top_level.txt +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_agent_workspace.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_atomic_file.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_background_index.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_cli.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_codex_delete.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_codex_index.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_codex_lineage.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_codex_transcript.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_config.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_config_cli.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_denested_transport_benchmark.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_diagnostics.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_discovery.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_display_transport.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_focus_highlight.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_fullscreen_binding.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_fuzzy.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_help_agent.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_help_workspace.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_history_cleanup.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_keymap.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_launcher.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_layout_profile.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_legacy_sessions.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_local_clipboard.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_local_open.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_modals.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_mode_selection.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_modes.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_mouse_manager.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_new_project.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_options.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_orphan_recovery.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_pane_surface.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_path_codec.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_projects_pane.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_release_notes.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_release_workflow.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_remote_config.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_renames.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_restart_state.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_running_pane.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_running_sort.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_runtime_config.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_scroll_agent.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_scroll_manager.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_selection_isolation.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_self_update.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_session_cache.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_session_index.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_session_lease.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_sessions_pane.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_setting_contracts.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_settings.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_sidebar.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_soft_quit.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_ssh_args.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_ssh_compat.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_ssh_display_benchmark.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_ssh_display_diagnostics.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_ssh_doctor.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_status.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_statusbar.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_system_deps.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_terminal_size.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_terminal_status.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_termux_local_touch.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_tmux_binding_manager.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_tmux_ctl.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_tmux_health.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_tmux_integration.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_tmux_server.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_tmux_status.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_tool_panes.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_transcript.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_utf8_state.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_web_demo_recorder.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_wheel_smoke.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_widgets.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tests/test_yolo_prompt.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tools/denested_transport_benchmark.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tools/release_notes.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev3}/tools/wheel_smoke.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.3.6.dev3"
|
|
@@ -270,8 +270,6 @@ class LocalHistoryView:
|
|
|
270
270
|
self,
|
|
271
271
|
previous: HistorySnapshot,
|
|
272
272
|
incoming: HistorySnapshot,
|
|
273
|
-
*,
|
|
274
|
-
prefer_deeper: bool = False,
|
|
275
273
|
) -> HistorySnapshot:
|
|
276
274
|
"""Retain already-fetched history when a later hot capture shrinks."""
|
|
277
275
|
if not self._same_geometry(
|
|
@@ -280,28 +278,13 @@ class LocalHistoryView:
|
|
|
280
278
|
return incoming
|
|
281
279
|
delta = self._timeline_delta(previous.lines, incoming.lines)
|
|
282
280
|
if delta is None:
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
# cache; keeping only its live tail would silently discard all
|
|
291
|
-
# intermediate output above the current screen.
|
|
292
|
-
lines = incoming.lines
|
|
293
|
-
else:
|
|
294
|
-
# Never splice two captures whose timelines cannot be aligned.
|
|
295
|
-
# Retaining old history and replacing only the live viewport
|
|
296
|
-
# looked conservative, but it silently omitted every row
|
|
297
|
-
# produced between those two pieces. That false seam appeared
|
|
298
|
-
# as discontinuous SSH history until returning to the bottom
|
|
299
|
-
# forced a fresh deep capture. Keep the newest internally
|
|
300
|
-
# contiguous hot capture instead; the first wheel-up requests
|
|
301
|
-
# a cumulative deep page and can recover older rows safely.
|
|
302
|
-
# An already-frozen viewport owns its immutable snapshot and
|
|
303
|
-
# therefore remains undisturbed by this cache replacement.
|
|
304
|
-
lines = incoming.lines
|
|
281
|
+
# Never splice two captures whose timelines cannot be aligned.
|
|
282
|
+
# Retaining old history and replacing only the live viewport
|
|
283
|
+
# looked conservative, but it silently omitted every row produced
|
|
284
|
+
# between those two pieces. Keep the newest internally contiguous
|
|
285
|
+
# capture instead. An already-frozen viewport owns its immutable
|
|
286
|
+
# snapshot and therefore remains undisturbed by cache replacement.
|
|
287
|
+
lines = incoming.lines
|
|
305
288
|
else:
|
|
306
289
|
start = min(0, delta)
|
|
307
290
|
incoming_end = delta + len(incoming.lines)
|
|
@@ -329,8 +312,6 @@ class LocalHistoryView:
|
|
|
329
312
|
def _remember_content(
|
|
330
313
|
self,
|
|
331
314
|
snapshot: HistorySnapshot,
|
|
332
|
-
*,
|
|
333
|
-
prefer_deeper: bool = False,
|
|
334
315
|
) -> HistorySnapshot:
|
|
335
316
|
assert snapshot.pane_id is not None
|
|
336
317
|
previous = self.content_cache.get(snapshot.pane_id)
|
|
@@ -348,8 +329,7 @@ class LocalHistoryView:
|
|
|
348
329
|
stored = self._merge_content(previous, snapshot) if anchored else snapshot
|
|
349
330
|
self._unverified_after_reconnect.discard(snapshot.pane_id)
|
|
350
331
|
else:
|
|
351
|
-
stored = self._merge_content(
|
|
352
|
-
previous, snapshot, prefer_deeper=prefer_deeper)
|
|
332
|
+
stored = self._merge_content(previous, snapshot)
|
|
353
333
|
# Reinsert an existing pane to keep insertion order as recency order.
|
|
354
334
|
self.content_cache.pop(snapshot.pane_id, None)
|
|
355
335
|
self.content_cache[snapshot.pane_id] = stored
|
|
@@ -485,7 +465,10 @@ class LocalHistoryView:
|
|
|
485
465
|
cached = route
|
|
486
466
|
loaded_limit = min(len(cached.lines), self.history_limit)
|
|
487
467
|
target_lines = min(self.history_limit, _HISTORY_INITIAL_LINES)
|
|
488
|
-
if
|
|
468
|
+
if (
|
|
469
|
+
cached.more_available
|
|
470
|
+
and loaded_limit < min(target_lines, _HISTORY_PREFETCH_LINES)
|
|
471
|
+
):
|
|
489
472
|
self.cancel_pane(route.pane_id)
|
|
490
473
|
request_id = self._allocate_request_id()
|
|
491
474
|
self._deep_pending[request_id] = _PendingHistory(
|
|
@@ -496,9 +479,9 @@ class LocalHistoryView:
|
|
|
496
479
|
max(1, initial_offset),
|
|
497
480
|
)
|
|
498
481
|
self._deep_requests += 1
|
|
499
|
-
#
|
|
500
|
-
# capture
|
|
501
|
-
#
|
|
482
|
+
# A byte-budgeted or older peer may return less than the normal
|
|
483
|
+
# coherent routing capture. Keep the live pane intact until one
|
|
484
|
+
# cumulative response arrives rather than expose a short suffix.
|
|
502
485
|
return HistoryAction(
|
|
503
486
|
protocol_frame=encode_history_request(
|
|
504
487
|
request_id, event.x, event.y, target_lines
|
|
@@ -525,22 +508,15 @@ class LocalHistoryView:
|
|
|
525
508
|
),
|
|
526
509
|
)
|
|
527
510
|
self.viewports[route.pane_id] = viewport
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
)
|
|
533
|
-
request_id = self._allocate_request_id()
|
|
534
|
-
self._deep_pending[request_id] = _PendingHistory(
|
|
535
|
-
self.route_epoch,
|
|
536
|
-
route.pane_id,
|
|
537
|
-
target_lines,
|
|
538
|
-
time.monotonic() if now is None else now,
|
|
539
|
-
)
|
|
540
|
-
self._deep_requests += 1
|
|
511
|
+
# A complete 300-row routing capture is already one coherent history
|
|
512
|
+
# source. Paint it synchronously, then fetch a cumulative page only as
|
|
513
|
+
# the viewport approaches its top. This removes an SSH round trip from
|
|
514
|
+
# the first wheel tick without reintroducing a hot/deep splice.
|
|
541
515
|
return HistoryAction(
|
|
542
|
-
protocol_frame=
|
|
543
|
-
|
|
516
|
+
protocol_frame=self._extend_history(
|
|
517
|
+
viewport,
|
|
518
|
+
now=now,
|
|
519
|
+
request_position=(event.x, event.y),
|
|
544
520
|
),
|
|
545
521
|
render_history=True,
|
|
546
522
|
)
|
|
@@ -550,6 +526,7 @@ class LocalHistoryView:
|
|
|
550
526
|
viewport: _HistoryViewport,
|
|
551
527
|
*,
|
|
552
528
|
now: float | None = None,
|
|
529
|
+
request_position: tuple[int, int] | None = None,
|
|
553
530
|
) -> bytes:
|
|
554
531
|
"""Request the next cumulative page when a viewport nears its top."""
|
|
555
532
|
snapshot = viewport.snapshot
|
|
@@ -583,7 +560,9 @@ class LocalHistoryView:
|
|
|
583
560
|
return b""
|
|
584
561
|
target_lines = min(
|
|
585
562
|
self.history_limit,
|
|
586
|
-
|
|
563
|
+
_HISTORY_INITIAL_LINES
|
|
564
|
+
if viewport.loaded_limit < _HISTORY_INITIAL_LINES
|
|
565
|
+
else viewport.loaded_limit + _HISTORY_PAGE_LINES,
|
|
587
566
|
)
|
|
588
567
|
request_id = self._allocate_request_id()
|
|
589
568
|
self._deep_pending[request_id] = _PendingHistory(
|
|
@@ -593,10 +572,14 @@ class LocalHistoryView:
|
|
|
593
572
|
requested_at,
|
|
594
573
|
)
|
|
595
574
|
self._deep_requests += 1
|
|
596
|
-
|
|
597
|
-
request_id,
|
|
575
|
+
request_x, request_y = request_position or (
|
|
598
576
|
snapshot.x + 1,
|
|
599
577
|
snapshot.y + 1,
|
|
578
|
+
)
|
|
579
|
+
return encode_history_request(
|
|
580
|
+
request_id,
|
|
581
|
+
request_x,
|
|
582
|
+
request_y,
|
|
600
583
|
target_lines,
|
|
601
584
|
)
|
|
602
585
|
|
|
@@ -911,7 +894,11 @@ class LocalHistoryView:
|
|
|
911
894
|
# snapshot instead of jumping to newer or unrelated text.
|
|
912
895
|
self._anchor_rejects += 1
|
|
913
896
|
return HistoryAction()
|
|
914
|
-
|
|
897
|
+
# The response is one cumulative capture and its exact visible anchor
|
|
898
|
+
# was just proved above. Replace the cached timeline as a whole instead
|
|
899
|
+
# of merging it with the older hot generation; this keeps every row's
|
|
900
|
+
# text and background styling from one server render.
|
|
901
|
+
stored = self._replace_content(snapshot)
|
|
915
902
|
stored_offset = self._aligned_offset(stored, anchor)
|
|
916
903
|
if stored_offset is None:
|
|
917
904
|
# The raw response was valid, so this can only be an ambiguous
|
|
@@ -960,7 +947,7 @@ class LocalHistoryView:
|
|
|
960
947
|
return matched_offset
|
|
961
948
|
|
|
962
949
|
# Agent status rows (for example a Codex spinner) can change between
|
|
963
|
-
# the
|
|
950
|
+
# the hot snapshot and a deep response. Requiring the
|
|
964
951
|
# entire visible viewport to remain byte-identical then strands the
|
|
965
952
|
# user at the hot-cache boundary. Align on a majority of stable,
|
|
966
953
|
# non-blank lines instead, but only when each evidence line occurs
|
|
@@ -2707,7 +2707,7 @@ def test_forced_policy_prefetch_preserves_other_pane_history_position():
|
|
|
2707
2707
|
assert view.viewports["%7"].offset == 1
|
|
2708
2708
|
|
|
2709
2709
|
|
|
2710
|
-
def
|
|
2710
|
+
def test_local_history_enters_warm_cache_immediately_then_prefetches_near_top():
|
|
2711
2711
|
view = LocalHistoryView()
|
|
2712
2712
|
prefetch = InputFrameDecoder().feed(view.begin_prefetch(1.0))[0]
|
|
2713
2713
|
request_id, _limit = decode_history_prefetch(prefetch.data)
|
|
@@ -2717,17 +2717,26 @@ def test_local_history_requests_deep_page_when_hot_viewport_fills_prefetch():
|
|
|
2717
2717
|
0,
|
|
2718
2718
|
0,
|
|
2719
2719
|
80,
|
|
2720
|
-
|
|
2721
|
-
(
|
|
2720
|
+
30,
|
|
2721
|
+
tuple(f"line-{index}".encode() for index in range(300)),
|
|
2722
2722
|
more_available=True,
|
|
2723
2723
|
)
|
|
2724
2724
|
view.accept_prefetch(HistoryBatch(request_id, (route,)))
|
|
2725
2725
|
|
|
2726
|
-
|
|
2727
|
-
|
|
2726
|
+
wheel = SgrMouseEvent(b"up", 64, 40, 2, True)
|
|
2727
|
+
action = view.wheel(wheel)
|
|
2728
2728
|
|
|
2729
|
+
assert action.render_history is True
|
|
2730
|
+
assert action.protocol_frame == b""
|
|
2731
|
+
assert view.overlays()[0][1][-1] == b"line-298"
|
|
2732
|
+
|
|
2733
|
+
request_action = HistoryAction()
|
|
2734
|
+
for _ in range(200):
|
|
2735
|
+
request_action = view.wheel(wheel)
|
|
2736
|
+
if request_action.protocol_frame:
|
|
2737
|
+
break
|
|
2738
|
+
request = InputFrameDecoder().feed(request_action.protocol_frame)[0]
|
|
2729
2739
|
assert decode_history_request(request.data)[3] == 2000
|
|
2730
|
-
assert action.forwarded_input == b""
|
|
2731
2740
|
|
|
2732
2741
|
|
|
2733
2742
|
def test_local_history_keeps_empty_plain_agent_wheel_from_tmux_copy_mode():
|
|
@@ -3594,7 +3603,7 @@ def test_unaligned_hot_prefetch_never_creates_a_discontinuous_history_seam():
|
|
|
3594
3603
|
0,
|
|
3595
3604
|
30,
|
|
3596
3605
|
3,
|
|
3597
|
-
tuple(f"old-{index}".encode() for index in range(
|
|
3606
|
+
tuple(f"old-{index}".encode() for index in range(200)),
|
|
3598
3607
|
more_available=True,
|
|
3599
3608
|
)
|
|
3600
3609
|
view.accept_prefetch(HistoryBatch(first_id, (old,)))
|
|
@@ -3608,14 +3617,14 @@ def test_unaligned_hot_prefetch_never_creates_a_discontinuous_history_seam():
|
|
|
3608
3617
|
0,
|
|
3609
3618
|
30,
|
|
3610
3619
|
3,
|
|
3611
|
-
tuple(f"new-{index}".encode() for index in range(
|
|
3620
|
+
tuple(f"new-{index}".encode() for index in range(200)),
|
|
3612
3621
|
more_available=True,
|
|
3613
3622
|
)
|
|
3614
3623
|
view.accept_prefetch(HistoryBatch(second_id, (current,)))
|
|
3615
3624
|
|
|
3616
3625
|
wheel = view.wheel(SgrMouseEvent(b"up", 64, 40, 2, True))
|
|
3617
3626
|
|
|
3618
|
-
# Do not
|
|
3627
|
+
# Do not expose a short/incomplete hot suffix. It has no anchor to the
|
|
3619
3628
|
# previous capture and an active provider may repaint again before the deep
|
|
3620
3629
|
# response, which used to leave a visible gap until returning to bottom.
|
|
3621
3630
|
assert not wheel.render_history
|
|
@@ -3635,14 +3644,14 @@ def test_unaligned_hot_prefetch_never_creates_a_discontinuous_history_seam():
|
|
|
3635
3644
|
0,
|
|
3636
3645
|
30,
|
|
3637
3646
|
3,
|
|
3638
|
-
tuple(f"deep-{index}".encode() for index in range(
|
|
3647
|
+
tuple(f"deep-{index}".encode() for index in range(1800)) + current.lines,
|
|
3639
3648
|
)
|
|
3640
3649
|
accepted = view.accept(deep)
|
|
3641
3650
|
|
|
3642
3651
|
assert accepted.render_history
|
|
3643
3652
|
assert view.viewports["%8"].snapshot.lines == deep.lines
|
|
3644
|
-
assert view.overlays()[0][1] == (b"new-
|
|
3645
|
-
assert b"old-
|
|
3653
|
+
assert view.overlays()[0][1] == (b"new-195", b"new-196", b"new-197")
|
|
3654
|
+
assert b"old-196" not in view.viewports["%8"].snapshot.lines
|
|
3646
3655
|
|
|
3647
3656
|
|
|
3648
3657
|
def test_initial_deep_history_retry_and_wheel_down_are_bounded():
|
|
@@ -3654,7 +3663,7 @@ def test_initial_deep_history_retry_and_wheel_down_are_bounded():
|
|
|
3654
3663
|
0,
|
|
3655
3664
|
30,
|
|
3656
3665
|
3,
|
|
3657
|
-
tuple(f"line-{index}".encode() for index in range(
|
|
3666
|
+
tuple(f"line-{index}".encode() for index in range(200)),
|
|
3658
3667
|
more_available=True,
|
|
3659
3668
|
)
|
|
3660
3669
|
view.visible_routes = (route,)
|
|
@@ -3740,6 +3749,12 @@ def test_periodic_hot_prefetch_does_not_shrink_a_reusable_deep_snapshot():
|
|
|
3740
3749
|
view.accept_prefetch(HistoryBatch(first_id, (hot,)))
|
|
3741
3750
|
wheel = SgrMouseEvent(b"up", 64, 40, 2, True)
|
|
3742
3751
|
deep_action = view.wheel(wheel)
|
|
3752
|
+
assert deep_action.render_history is True
|
|
3753
|
+
assert deep_action.protocol_frame == b""
|
|
3754
|
+
for _ in range(300):
|
|
3755
|
+
deep_action = view.wheel(wheel)
|
|
3756
|
+
if deep_action.protocol_frame:
|
|
3757
|
+
break
|
|
3743
3758
|
deep_id = decode_history_request(
|
|
3744
3759
|
InputFrameDecoder().feed(deep_action.protocol_frame)[0].data
|
|
3745
3760
|
)[0]
|
|
@@ -3788,10 +3803,11 @@ def test_validated_deep_capture_recovers_unanchored_rewind_output():
|
|
|
3788
3803
|
first_id, _limit = decode_history_prefetch(first.data)
|
|
3789
3804
|
repeated = b"\033[48;2;33;58;43mrewind-row"
|
|
3790
3805
|
hot_lines = (
|
|
3791
|
-
*(f"old-{index}".encode() for index in range(
|
|
3806
|
+
*(f"old-{index}".encode() for index in range(120)),
|
|
3792
3807
|
repeated,
|
|
3793
3808
|
repeated,
|
|
3794
3809
|
repeated,
|
|
3810
|
+
*(f"new-{index}".encode() for index in range(176)),
|
|
3795
3811
|
b"live-bottom",
|
|
3796
3812
|
)
|
|
3797
3813
|
hot = HistorySnapshot(
|
|
@@ -3805,7 +3821,14 @@ def test_validated_deep_capture_recovers_unanchored_rewind_output():
|
|
|
3805
3821
|
more_available=True,
|
|
3806
3822
|
)
|
|
3807
3823
|
view.accept_prefetch(HistoryBatch(first_id, (hot,)))
|
|
3808
|
-
|
|
3824
|
+
wheel_up = SgrMouseEvent(b"up", 64, 40, 2, True)
|
|
3825
|
+
wheel = view.wheel(wheel_up)
|
|
3826
|
+
assert wheel.render_history is True
|
|
3827
|
+
assert wheel.protocol_frame == b""
|
|
3828
|
+
for _ in range(300):
|
|
3829
|
+
wheel = view.wheel(wheel_up)
|
|
3830
|
+
if wheel.protocol_frame:
|
|
3831
|
+
break
|
|
3809
3832
|
request_id = decode_history_request(
|
|
3810
3833
|
InputFrameDecoder().feed(wheel.protocol_frame)[0].data
|
|
3811
3834
|
)[0]
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.3.6.dev2"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|