Skip to content

[SCR-560] Fix REPL scrollback selection, path linkification, Saved-to display, and binary output handling#30

Open
sahilsunny wants to merge 6 commits into
mainfrom
fix/repl/selection-saved-path-display/SCR-560
Open

[SCR-560] Fix REPL scrollback selection, path linkification, Saved-to display, and binary output handling#30
sahilsunny wants to merge 6 commits into
mainfrom
fix/repl/selection-saved-path-display/SCR-560

Conversation

@sahilsunny

Copy link
Copy Markdown
Collaborator

Summary

Fix REPL scrollback selection, path linkification, Saved-to display, and binary output handling [SCR-560]. Drag-copy dropped the last selected character, relative paths were not clickable or fully highlighted, and binary responses (e.g. screenshots) polluted scrollback with raw bytes instead of a summary.

Details

Selection bounds (src/scrapingbee_cli/interactive.py:457-471, 2960-2965, 3347-3353)

  • Add _selection_bounds() at interactive.py:457-471 to convert inclusive mouse endpoints into half-open [lo, hi) slice bounds (+1 on the high char) so _slice_selection and _styled_with_selection include the character under the cursor.
  • Use _selection_bounds in the MOUSE_UP copy path (interactive.py:2960-2965) and _scrollback_render highlight path (interactive.py:3347-3353) so copy and highlight stay in sync.

Path link detection (src/scrapingbee_cli/interactive.py:474-491, 3042-3072, 3091-3104)

  • Add module-level _resolve_path_str() at interactive.py:474-479 to expand ~ and resolve relative paths against cwd (replacing the narrower nested helper that only handled ./ and ../).
  • Add _REL_PATH_RE at interactive.py:484-491 for relative paths (abc/out.png) and bare filenames (shot.png).
  • Extend _existing_paths_in() at interactive.py:3042-3072 to scan both absolute (_path_start_re) and relative matches with overlap deduplication.
  • Allow _styled_with_links() at interactive.py:3103-3104 to scan rows containing . so bare filenames get underlined.

Saved-to path display (src/scrapingbee_cli/cli_utils.py:28-37, 1883-1885; theme.py:815-817; commands/crawl.py:625-642)

  • Add display_path() at cli_utils.py:28-37 to normalise user-facing paths to absolute form via Path.expanduser().resolve().
  • Apply display_path in write_output() Saved-to line (cli_utils.py:1883-1885), theme.print_completion_summary Output row (theme.py:815-817), and all crawl save-report messages (crawl.py:625-642).

Binary REPL preview (src/scrapingbee_cli/cli_utils.py:48-119, 1890-1912)

  • Add _BINARY_MAGICS and _is_text_payload() at cli_utils.py:48-77 with magic-byte, NUL, and control-char heuristics so PNG and other binaries are not misclassified as text.
  • Refactor _repl_cache_path() at cli_utils.py:80-85; always cache REPL responses to last-output in _maybe_repl_preview() at cli_utils.py:88-119, but return empty stdout + binary summary for non-text payloads instead of dumping raw bytes.
  • Show :view hint only for text; binary gets --output-file guidance at cli_utils.py:1902-1912. Non-REPL / piped behaviour unchanged.

Tests

  • test_scrollback_selection.py:93-142 — unit tests for _selection_bounds, _resolve_path_str, _REL_PATH_RE.
  • test_cli_utils.py:420-433, 542-567 — display_path, Saved-to absolute path, binary REPL preview, non-REPL unchanged.
  • test_cli.py:844-878 — crawl Saved-to prints absolute path.
  • test_repl_pty.py:45-87, 168-262 — end-to-end drag-copy of Saved to screenshot path via mock API and fake clipboard tools on PATH.

Fix REPL scrollback selection, path linkification, Saved-to display, and binary output handling [SCR-560]. Drag-copy dropped the last selected character, relative paths were not clickable or fully highlighted, and binary responses (e.g. screenshots) polluted scrollback with raw bytes instead of a summary.

Details
Selection bounds (src/scrapingbee_cli/interactive.py:457-471, 2960-2965, 3347-3353)
- Add _selection_bounds() at interactive.py:457-471 to convert inclusive mouse endpoints into half-open [lo, hi) slice bounds (+1 on the high char) so _slice_selection and _styled_with_selection include the character under the cursor.
- Use _selection_bounds in the MOUSE_UP copy path (interactive.py:2960-2965) and _scrollback_render highlight path (interactive.py:3347-3353) so copy and highlight stay in sync.

Path link detection (src/scrapingbee_cli/interactive.py:474-491, 3042-3072, 3091-3104)
- Add module-level _resolve_path_str() at interactive.py:474-479 to expand ~ and resolve relative paths against cwd (replacing the narrower nested helper that only handled ./ and ../).
- Add _REL_PATH_RE at interactive.py:484-491 for relative paths (abc/out.png) and bare filenames (shot.png).
- Extend _existing_paths_in() at interactive.py:3042-3072 to scan both absolute (_path_start_re) and relative matches with overlap deduplication.
- Allow _styled_with_links() at interactive.py:3103-3104 to scan rows containing . so bare filenames get underlined.

Saved-to path display (src/scrapingbee_cli/cli_utils.py:28-37, 1883-1885; theme.py:815-817; commands/crawl.py:625-642)
- Add display_path() at cli_utils.py:28-37 to normalise user-facing paths to absolute form via Path.expanduser().resolve().
- Apply display_path in write_output() Saved-to line (cli_utils.py:1883-1885), theme.print_completion_summary Output row (theme.py:815-817), and all crawl save-report messages (crawl.py:625-642).

Binary REPL preview (src/scrapingbee_cli/cli_utils.py:48-119, 1890-1912)
- Add _BINARY_MAGICS and _is_text_payload() at cli_utils.py:48-77 with magic-byte, NUL, and control-char heuristics so PNG and other binaries are not misclassified as text.
- Refactor _repl_cache_path() at cli_utils.py:80-85; always cache REPL responses to last-output in _maybe_repl_preview() at cli_utils.py:88-119, but return empty stdout + binary summary for non-text payloads instead of dumping raw bytes.
- Show :view hint only for text; binary gets --output-file guidance at cli_utils.py:1902-1912. Non-REPL / piped behaviour unchanged.

Tests
- test_scrollback_selection.py:93-142 — unit tests for _selection_bounds, _resolve_path_str, _REL_PATH_RE.
- test_cli_utils.py:420-433, 542-567 — display_path, Saved-to absolute path, binary REPL preview, non-REPL unchanged.
- test_cli.py:844-878 — crawl Saved-to prints absolute path.
- test_repl_pty.py:45-87, 168-262 — end-to-end drag-copy of Saved to screenshot path via mock API and fake clipboard tools on PATH.
@sahilsunny sahilsunny self-assigned this Jul 10, 2026
Bump package version to v1.5.1 and document the SCR-560 REPL fixes in the changelog.

Details
Package metadata (pyproject.toml:7, src/scrapingbee_cli/__init__.py:6-15, uv.lock)
- Set project/__version__ to 1.5.1 so CLI --version and User-Agent-Client-Version stay aligned.

Changelog and agent docs (CHANGELOG.md, AGENTS.md:14)
- Add [1.5.1] - 2026-07-10 Fixed entries for drag-copy last character, relative path linkification, and binary REPL preview.
- Raise AGENTS.md upgrade threshold to < 1.5.1.

Plugins and skills (.claude-plugin/marketplace.json:15, plugins/.../plugin.json:4, .agents/skills/*/SKILL.md + synced copies)
- Bump plugin/skill frontmatter versions to 1.5.1 and sync via scripts/sync-skills.sh.
Make path unit tests cross-platform so Windows CI passes.

Details
tests/unit/test_cli_utils.py:420-434
- Assert Path(abs_path).is_absolute() instead of startswith("/") — Windows absolute paths use drive letters (C:\...), not a leading slash.

tests/unit/test_scrollback_selection.py:125-130
- Point HOME and USERPROFILE at tmp_path and compare against pathlib, so os.path.expanduser works on both Unix and Windows.
Fix the remaining cross-platform and flaky CI failures.

Details
- Normalize expanded paths with abspath so Windows paths do not retain mixed separators.
- Use shorter usage help output in the PTY warning test so the warning stays on-screen deterministically.
Summary
- Fix remaining flaky CI failure in test_session_default_skip_warning_on_screen
  (macOS, Python 3.12).

Details
- tests/unit/test_repl_pty.py (lines 421-446): added _pump_until_transient,
  which feeds PTY output to pyte in small slices and checks the predicate
  after each slice. The skip warning renders before the help output streams
  in, so on a fast runner it could scroll off or be repainted within a
  single 64 KiB read, making the final-screen-only check in _pump_until
  miss it entirely.
- tests/unit/test_repl_pty.py (lines 449-476): the skip-warning test now
  uses _pump_until_transient so every intermediate screen state is checked.
Summary
- Deflake test_session_default_skip_warning_on_screen for good: the previous
  run still failed on ubuntu/Python 3.12 after the transient-pump fix.

Details
- tests/unit/test_repl_pty.py (line 93): _spawn now accepts rows/cols
  overrides so individual tests can size the PTY grid.
- tests/unit/test_repl_pty.py (lines 448-476): the skip-warning test spawns
  a 64-row grid. The warning is appended to scrollback before the command
  output, so on a 32-row screen later lines (help text, footer, background
  usage-refresh output on CI) can push it out of the visible window before
  the predicate observes it. The failure message now includes the final
  screen dump for easier CI debugging.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants