railmux 0.3.6.dev2__tar.gz → 0.3.6.dev4__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.dev4}/PKG-INFO +1 -1
- railmux-0.3.6.dev4/src/railmux/__init__.py +1 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/fast_display_history.py +39 -52
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/fast_display_server.py +33 -16
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4/src/railmux.egg-info}/PKG-INFO +1 -1
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_fast_full_window.py +86 -33
- railmux-0.3.6.dev2/src/railmux/__init__.py +0 -1
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/LICENSE +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/MANIFEST.in +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/README.md +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/pyproject.toml +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/setup.cfg +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/__main__.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/atomic_file.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/background_index.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/cli.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/codex_index.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/config.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/config_cli.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/diagnostics.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/discovery.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/display_transport.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/fast_display_client.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/fast_display_input.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/fast_display_protocol.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/favorites.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/fuzzy.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/help_workspace.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/launcher.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/legacy_sessions.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/local_clipboard.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/local_open.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/models.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/modes.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/mouse_manager.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/orphan_marker.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/pane_surface.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/path_codec.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/remote_config.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/renames.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/restart_state.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/runtime_config.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/scroll_agent.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/scroll_manager.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/selection_isolation.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/self_update.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/session_cache.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/session_index.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/session_lease.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/setting_contracts.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/settings.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/ssh_args.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/ssh_compat.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/ssh_display_diagnostics.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/ssh_doctor.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/system_deps.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/terminal_status.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/tmux_binding_manager.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/tmux_ctl.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/tmux_health.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/tmux_server.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/tool_panes.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/transcript.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/ui/__init__.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/ui/_widgets.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/ui/app.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/ui/keymap.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/ui/modals.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/ui/projects_pane.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/ui/running_pane.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/ui/sessions_pane.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/ui/sidebar.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/ui/statusbar.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux/ui/workspace.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux.egg-info/SOURCES.txt +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux.egg-info/dependency_links.txt +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux.egg-info/entry_points.txt +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux.egg-info/requires.txt +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/src/railmux.egg-info/top_level.txt +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_agent_workspace.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_atomic_file.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_background_index.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_cli.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_codex_delete.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_codex_index.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_codex_lineage.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_codex_transcript.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_config.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_config_cli.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_denested_transport_benchmark.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_diagnostics.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_discovery.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_display_transport.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_focus_highlight.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_fullscreen_binding.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_fuzzy.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_help_agent.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_help_workspace.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_history_cleanup.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_keymap.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_launcher.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_layout_profile.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_legacy_sessions.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_local_clipboard.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_local_open.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_modals.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_mode_selection.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_modes.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_mouse_manager.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_new_project.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_options.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_orphan_recovery.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_pane_surface.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_path_codec.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_projects_pane.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_release_notes.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_release_workflow.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_remote_config.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_renames.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_restart_state.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_running_pane.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_running_sort.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_runtime_config.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_scroll_agent.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_scroll_manager.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_selection_isolation.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_self_update.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_session_cache.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_session_index.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_session_lease.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_sessions_pane.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_setting_contracts.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_settings.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_sidebar.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_soft_quit.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_ssh_args.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_ssh_compat.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_ssh_display_benchmark.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_ssh_display_diagnostics.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_ssh_doctor.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_status.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_statusbar.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_system_deps.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_terminal_size.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_terminal_status.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_termux_local_touch.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_tmux_binding_manager.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_tmux_ctl.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_tmux_health.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_tmux_integration.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_tmux_server.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_tmux_status.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_tool_panes.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_transcript.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_utf8_state.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_web_demo_recorder.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_wheel_smoke.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_widgets.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tests/test_yolo_prompt.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tools/denested_transport_benchmark.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tools/release_notes.py +0 -0
- {railmux-0.3.6.dev2 → railmux-0.3.6.dev4}/tools/wheel_smoke.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.3.6.dev4"
|
|
@@ -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
|
|
@@ -1171,12 +1171,35 @@ def apply_path_open_request(
|
|
|
1171
1171
|
)
|
|
1172
1172
|
|
|
1173
1173
|
|
|
1174
|
-
def
|
|
1175
|
-
|
|
1174
|
+
def _render_history_lines(
|
|
1175
|
+
pyte: object,
|
|
1176
|
+
lines: Sequence[bytes],
|
|
1177
|
+
width: int,
|
|
1178
|
+
) -> tuple[bytes, ...]:
|
|
1179
|
+
"""Render physical tmux rows while retaining cross-row terminal style.
|
|
1180
|
+
|
|
1181
|
+
``tmux capture-pane -e`` is a terminal stream, not a collection of
|
|
1182
|
+
independently styled strings. In particular, tmux omits an SGR prefix
|
|
1183
|
+
when a row inherits the previous row's foreground, background, or text
|
|
1184
|
+
attributes. Keep one decoder alive for the ordered capture, but clear its
|
|
1185
|
+
one-row cell buffer between rows. :func:`render_rows` then makes every
|
|
1186
|
+
result independently paintable for the local history overlay.
|
|
1187
|
+
"""
|
|
1176
1188
|
screen = pyte.Screen(width, 1)
|
|
1177
1189
|
stream = pyte.ByteStream(screen)
|
|
1178
|
-
|
|
1179
|
-
|
|
1190
|
+
rendered: list[bytes] = []
|
|
1191
|
+
for line in lines:
|
|
1192
|
+
screen.buffer[0].clear()
|
|
1193
|
+
screen.cursor.x = 0
|
|
1194
|
+
screen.cursor.y = 0
|
|
1195
|
+
stream.feed(line)
|
|
1196
|
+
rendered.append(render_rows(screen)[0])
|
|
1197
|
+
return tuple(rendered)
|
|
1198
|
+
|
|
1199
|
+
|
|
1200
|
+
def _render_history_line(pyte: object, line: bytes, width: int) -> bytes:
|
|
1201
|
+
"""Render one self-contained physical row with allowlisted SGR styling."""
|
|
1202
|
+
return _render_history_lines(pyte, (line,), width)[0]
|
|
1180
1203
|
|
|
1181
1204
|
|
|
1182
1205
|
def _read_transcript_tail(fd: int) -> tuple[str, bool]:
|
|
@@ -1352,7 +1375,8 @@ def _capture_pane_history(
|
|
|
1352
1375
|
# Retain the newest suffix when the styled representation reaches its byte
|
|
1353
1376
|
# budget. Iterate backwards so an oversized response never sacrifices the
|
|
1354
1377
|
# pane's current viewport merely to retain older scrollback.
|
|
1355
|
-
|
|
1378
|
+
rendered_raw = _render_history_lines(pyte, raw_lines, pane.width)
|
|
1379
|
+
current_raw = rendered_raw[-pane.height :]
|
|
1356
1380
|
transcript_entry = None
|
|
1357
1381
|
# ``None`` is retained only for old/test geometry that predates the
|
|
1358
1382
|
# provider field; every validated live marker carries an explicit value.
|
|
@@ -1383,21 +1407,14 @@ def _capture_pane_history(
|
|
|
1383
1407
|
transcript_lines = transcript_entry.rows
|
|
1384
1408
|
history_count = max(0, max_lines - pane.height)
|
|
1385
1409
|
source_lines = (
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
else ()
|
|
1389
|
-
) + tuple((line, False) for line in current_raw)
|
|
1410
|
+
transcript_lines[-history_count:] if history_count else ()
|
|
1411
|
+
) + current_raw
|
|
1390
1412
|
else:
|
|
1391
|
-
source_lines =
|
|
1413
|
+
source_lines = rendered_raw
|
|
1392
1414
|
newest_first: list[bytes] = []
|
|
1393
1415
|
packed_size = 2 # history line-count prefix
|
|
1394
1416
|
budget_truncated = False
|
|
1395
|
-
for
|
|
1396
|
-
rendered = (
|
|
1397
|
-
raw_line
|
|
1398
|
-
if rendered_already
|
|
1399
|
-
else _render_history_line(pyte, raw_line, pane.width)
|
|
1400
|
-
)
|
|
1417
|
+
for rendered in reversed(source_lines[-max_lines:]):
|
|
1401
1418
|
line_size = 4 + len(rendered)
|
|
1402
1419
|
if packed_size + line_size > _HISTORY_SNAPSHOT_RAW_BUDGET:
|
|
1403
1420
|
budget_truncated = True
|
|
@@ -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
|
+
|
|
2729
|
+
assert action.render_history is True
|
|
2730
|
+
assert action.protocol_frame == b""
|
|
2731
|
+
assert view.overlays()[0][1][-1] == b"line-298"
|
|
2728
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]
|
|
@@ -6954,8 +6977,8 @@ def test_server_history_capture_honours_limits_above_the_old_4096_cap(
|
|
|
6954
6977
|
)
|
|
6955
6978
|
monkeypatch.setattr(
|
|
6956
6979
|
fast_display_server,
|
|
6957
|
-
"
|
|
6958
|
-
lambda _pyte,
|
|
6980
|
+
"_render_history_lines",
|
|
6981
|
+
lambda _pyte, lines, _width: tuple(lines),
|
|
6959
6982
|
)
|
|
6960
6983
|
|
|
6961
6984
|
snapshot = fast_display_server._capture_pane_history(object(), pane, 7, 5000)
|
|
@@ -6996,16 +7019,31 @@ def test_server_raw_styled_hot_and_deep_history_keep_codex_foreground(
|
|
|
6996
7019
|
b"\033[38;2;205;214;244mvalue = 1\033[39m"
|
|
6997
7020
|
)
|
|
6998
7021
|
monochrome = (
|
|
6999
|
-
|
|
7022
|
+
# tmux omits the leading background because this physical row inherits
|
|
7023
|
+
# it from ``highlighted``. History parsing must keep that stream state
|
|
7024
|
+
# while still returning an independently paintable row.
|
|
7025
|
+
b" 3 \033[0m\033[38;5;2m\033[48;5;22m"
|
|
7000
7026
|
b"+#!/bin/bash\033[39m"
|
|
7001
7027
|
)
|
|
7028
|
+
removed = (
|
|
7029
|
+
b"\033[48;5;52m 4 \033[0m\033[38;5;1m\033[48;5;52m"
|
|
7030
|
+
b"-removed = True\033[39m"
|
|
7031
|
+
)
|
|
7032
|
+
removed_inherited = (
|
|
7033
|
+
b" 5 \033[0m\033[38;5;1m\033[48;5;52m"
|
|
7034
|
+
b"-removed = False\033[39m"
|
|
7035
|
+
)
|
|
7036
|
+
ordinary = b"\033[0m ordinary output"
|
|
7002
7037
|
raw = b"\n".join((
|
|
7003
7038
|
*(f"old-{index}".encode() for index in range(99)),
|
|
7004
7039
|
wrap_head,
|
|
7005
7040
|
wrap_continuation,
|
|
7006
|
-
*(f"line-{index}".encode() for index in range(
|
|
7041
|
+
*(f"line-{index}".encode() for index in range(294)),
|
|
7007
7042
|
highlighted,
|
|
7008
7043
|
monochrome,
|
|
7044
|
+
removed,
|
|
7045
|
+
removed_inherited,
|
|
7046
|
+
ordinary,
|
|
7009
7047
|
)) + b"\n"
|
|
7010
7048
|
monkeypatch.setattr(
|
|
7011
7049
|
subprocess, "check_output", lambda *_args, **_kwargs: raw)
|
|
@@ -7031,19 +7069,32 @@ def test_server_raw_styled_hot_and_deep_history_keep_codex_foreground(
|
|
|
7031
7069
|
terminal.ByteStream(screen).feed(row)
|
|
7032
7070
|
return screen
|
|
7033
7071
|
|
|
7034
|
-
highlighted_screen = styled_row(deep.lines[-
|
|
7035
|
-
monochrome_screen = styled_row(deep.lines[-
|
|
7072
|
+
highlighted_screen = styled_row(deep.lines[-5])
|
|
7073
|
+
monochrome_screen = styled_row(deep.lines[-4])
|
|
7074
|
+
removed_screen = styled_row(deep.lines[-3])
|
|
7075
|
+
removed_inherited_screen = styled_row(deep.lines[-2])
|
|
7076
|
+
ordinary_screen = styled_row(deep.lines[-1])
|
|
7036
7077
|
highlighted_start = highlighted_screen.display[0].index("value")
|
|
7037
7078
|
monochrome_start = monochrome_screen.display[0].index("#!/bin/bash")
|
|
7038
7079
|
assert highlighted_screen.buffer[0][highlighted_start].fg == "cdd6f4"
|
|
7039
7080
|
assert monochrome_screen.buffer[0][monochrome_start].fg == "00cd00"
|
|
7081
|
+
assert highlighted_screen.buffer[0][0].bg == "005f00"
|
|
7082
|
+
assert monochrome_screen.buffer[0][0].bg == "005f00"
|
|
7083
|
+
assert removed_screen.buffer[0][0].bg == "5f0000"
|
|
7084
|
+
assert removed_inherited_screen.buffer[0][0].bg == "5f0000"
|
|
7085
|
+
assert ordinary_screen.buffer[0][0].bg == "default"
|
|
7040
7086
|
coloured_backgrounds = {
|
|
7041
7087
|
char.bg
|
|
7042
|
-
for screen in (
|
|
7088
|
+
for screen in (
|
|
7089
|
+
highlighted_screen,
|
|
7090
|
+
monochrome_screen,
|
|
7091
|
+
removed_screen,
|
|
7092
|
+
removed_inherited_screen,
|
|
7093
|
+
)
|
|
7043
7094
|
for char in screen.buffer[0].values()
|
|
7044
7095
|
if char.bg != "default"
|
|
7045
7096
|
}
|
|
7046
|
-
assert coloured_backgrounds == {"005f00"}
|
|
7097
|
+
assert coloured_backgrounds == {"005f00", "5f0000"}
|
|
7047
7098
|
|
|
7048
7099
|
|
|
7049
7100
|
def test_server_claude_history_uses_stable_transcript_suffix(monkeypatch):
|
|
@@ -7074,8 +7125,8 @@ def test_server_claude_history_uses_stable_transcript_suffix(monkeypatch):
|
|
|
7074
7125
|
)
|
|
7075
7126
|
monkeypatch.setattr(
|
|
7076
7127
|
fast_display_server,
|
|
7077
|
-
"
|
|
7078
|
-
lambda _pyte,
|
|
7128
|
+
"_render_history_lines",
|
|
7129
|
+
lambda _pyte, lines, _width: tuple(lines),
|
|
7079
7130
|
)
|
|
7080
7131
|
|
|
7081
7132
|
hot = fast_display_server._capture_pane_history(object(), pane, 1, 300)
|
|
@@ -7117,8 +7168,8 @@ def test_server_waits_for_local_choice_before_rendering_claude_transcript(
|
|
|
7117
7168
|
)
|
|
7118
7169
|
monkeypatch.setattr(
|
|
7119
7170
|
fast_display_server,
|
|
7120
|
-
"
|
|
7121
|
-
lambda _pyte,
|
|
7171
|
+
"_render_history_lines",
|
|
7172
|
+
lambda _pyte, lines, _width: tuple(lines),
|
|
7122
7173
|
)
|
|
7123
7174
|
|
|
7124
7175
|
snapshot = fast_display_server._capture_pane_history(object(), pane, 1, 300)
|
|
@@ -7165,8 +7216,8 @@ def test_server_codex_history_uses_canonical_transcript_after_rewind(
|
|
|
7165
7216
|
)
|
|
7166
7217
|
monkeypatch.setattr(
|
|
7167
7218
|
fast_display_server,
|
|
7168
|
-
"
|
|
7169
|
-
lambda _pyte,
|
|
7219
|
+
"_render_history_lines",
|
|
7220
|
+
lambda _pyte, lines, _width: tuple(lines),
|
|
7170
7221
|
)
|
|
7171
7222
|
|
|
7172
7223
|
snapshot = fast_display_server._capture_pane_history(
|
|
@@ -7207,8 +7258,8 @@ def test_server_unreadable_claude_transcript_preserves_native_wheel_fallback(
|
|
|
7207
7258
|
)
|
|
7208
7259
|
monkeypatch.setattr(
|
|
7209
7260
|
fast_display_server,
|
|
7210
|
-
"
|
|
7211
|
-
lambda _pyte,
|
|
7261
|
+
"_render_history_lines",
|
|
7262
|
+
lambda _pyte, lines, _width: tuple(lines),
|
|
7212
7263
|
)
|
|
7213
7264
|
|
|
7214
7265
|
snapshot = fast_display_server._capture_pane_history(object(), pane, 1, 300)
|
|
@@ -7291,8 +7342,10 @@ def test_server_history_capture_truncates_to_newest_styled_byte_budget(
|
|
|
7291
7342
|
line_bytes = fast_display_server._HISTORY_SNAPSHOT_RAW_BUDGET // 3 + 100
|
|
7292
7343
|
monkeypatch.setattr(
|
|
7293
7344
|
fast_display_server,
|
|
7294
|
-
"
|
|
7295
|
-
lambda _pyte,
|
|
7345
|
+
"_render_history_lines",
|
|
7346
|
+
lambda _pyte, lines, _width: tuple(
|
|
7347
|
+
line + b"x" * line_bytes for line in lines
|
|
7348
|
+
),
|
|
7296
7349
|
)
|
|
7297
7350
|
|
|
7298
7351
|
snapshot = fast_display_server._capture_pane_history(object(), pane, 7, 20000)
|
|
@@ -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
|