Skip to content

Commit 127b4f8

Browse files
committed
Pane(feat[capture_frame]): Add capture_frame() method
why: Enable capturing pane content as TextFrame for visualization and snapshot testing. This bridges capture_pane() with the TextFrame dataclass for a more ergonomic testing workflow. what: - Add capture_frame() method that wraps capture_pane() - Default to pane dimensions when width/height not specified - Default to truncate mode for robustness in CI environments - Add comprehensive docstring with examples
1 parent a064b5a commit 127b4f8

File tree

1 file changed

+89
-0
lines changed

1 file changed

+89
-0
lines changed

src/libtmux/pane.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
import types
3232

3333
from libtmux._internal.types import StrPath
34+
from libtmux.textframe import TextFrame
35+
from libtmux.textframe.core import OverflowBehavior
3436

3537
from .server import Server
3638
from .session import Session
@@ -357,6 +359,93 @@ def capture_pane(
357359
cmd.extend(["-E", str(end)])
358360
return self.cmd(*cmd).stdout
359361

362+
def capture_frame(
363+
self,
364+
start: t.Literal["-"] | int | None = None,
365+
end: t.Literal["-"] | int | None = None,
366+
*,
367+
content_width: int | None = None,
368+
content_height: int | None = None,
369+
overflow_behavior: OverflowBehavior = "truncate",
370+
) -> TextFrame:
371+
"""Capture pane content as a TextFrame.
372+
373+
Combines :meth:`capture_pane` with :class:`~libtmux.textframe.TextFrame`
374+
for visualization and snapshot testing.
375+
376+
Parameters
377+
----------
378+
start : str | int, optional
379+
Starting line number (same as :meth:`capture_pane`).
380+
Zero is the first line of the visible pane.
381+
Positive numbers are lines in the visible pane.
382+
Negative numbers are lines in the history.
383+
``-`` is the start of the history.
384+
Default: None
385+
end : str | int, optional
386+
Ending line number (same as :meth:`capture_pane`).
387+
Zero is the first line of the visible pane.
388+
Positive numbers are lines in the visible pane.
389+
Negative numbers are lines in the history.
390+
``-`` is the end of the visible pane.
391+
Default: None
392+
content_width : int, optional
393+
Frame width. Defaults to pane's current width.
394+
content_height : int, optional
395+
Frame height. Defaults to pane's current height.
396+
overflow_behavior : OverflowBehavior, optional
397+
How to handle content that exceeds frame dimensions.
398+
Defaults to ``"truncate"`` since pane content may exceed
399+
nominal dimensions during terminal transitions.
400+
401+
Returns
402+
-------
403+
:class:`~libtmux.textframe.TextFrame`
404+
Frame containing captured pane content.
405+
406+
Examples
407+
--------
408+
>>> pane.send_keys('echo "Hello"', enter=True)
409+
>>> import time; time.sleep(0.1)
410+
>>> frame = pane.capture_frame(content_width=20, content_height=5)
411+
>>> 'Hello' in frame.render()
412+
True
413+
414+
>>> print(frame.render()) # doctest: +SKIP
415+
+--------------------+
416+
|$ echo "Hello" |
417+
|Hello |
418+
|$ |
419+
| |
420+
| |
421+
+--------------------+
422+
"""
423+
from libtmux.textframe import TextFrame as TextFrameClass
424+
425+
# Capture content
426+
lines = self.capture_pane(start=start, end=end)
427+
428+
# Use pane dimensions if not specified
429+
self.refresh()
430+
width = (
431+
content_width if content_width is not None else int(self.pane_width or 80)
432+
)
433+
height = (
434+
content_height
435+
if content_height is not None
436+
else int(self.pane_height or 24)
437+
)
438+
439+
# Create and populate frame
440+
frame = TextFrameClass(
441+
content_width=width,
442+
content_height=height,
443+
overflow_behavior=overflow_behavior,
444+
)
445+
frame.set_content(lines)
446+
447+
return frame
448+
360449
def send_keys(
361450
self,
362451
cmd: str,

0 commit comments

Comments
 (0)