euporie 2.3.2__py3-none-any.whl → 2.4.1__py3-none-any.whl

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.
Files changed (92) hide show
  1. euporie/console/__main__.py +3 -1
  2. euporie/console/app.py +6 -4
  3. euporie/console/tabs/console.py +34 -9
  4. euporie/core/__init__.py +6 -1
  5. euporie/core/__main__.py +1 -1
  6. euporie/core/app.py +79 -109
  7. euporie/core/border.py +44 -14
  8. euporie/core/comm/base.py +5 -4
  9. euporie/core/comm/ipywidgets.py +11 -11
  10. euporie/core/comm/registry.py +12 -6
  11. euporie/core/commands.py +30 -23
  12. euporie/core/completion.py +1 -4
  13. euporie/core/config.py +15 -5
  14. euporie/core/convert/{base.py → core.py} +117 -53
  15. euporie/core/convert/formats/ansi.py +46 -25
  16. euporie/core/convert/formats/base64.py +3 -3
  17. euporie/core/convert/formats/common.py +38 -13
  18. euporie/core/convert/formats/formatted_text.py +54 -12
  19. euporie/core/convert/formats/html.py +5 -5
  20. euporie/core/convert/formats/jpeg.py +1 -1
  21. euporie/core/convert/formats/markdown.py +4 -4
  22. euporie/core/convert/formats/pdf.py +1 -1
  23. euporie/core/convert/formats/pil.py +5 -3
  24. euporie/core/convert/formats/png.py +7 -6
  25. euporie/core/convert/formats/rich.py +4 -3
  26. euporie/core/convert/formats/sixel.py +5 -5
  27. euporie/core/convert/utils.py +1 -1
  28. euporie/core/current.py +11 -5
  29. euporie/core/formatted_text/ansi.py +4 -8
  30. euporie/core/formatted_text/html.py +1630 -856
  31. euporie/core/formatted_text/markdown.py +177 -166
  32. euporie/core/formatted_text/table.py +20 -14
  33. euporie/core/formatted_text/utils.py +21 -10
  34. euporie/core/io.py +14 -14
  35. euporie/core/kernel.py +48 -37
  36. euporie/core/key_binding/bindings/micro.py +5 -1
  37. euporie/core/key_binding/bindings/mouse.py +2 -2
  38. euporie/core/keys.py +3 -0
  39. euporie/core/launch.py +5 -2
  40. euporie/core/lexers.py +13 -2
  41. euporie/core/log.py +135 -139
  42. euporie/core/margins.py +32 -14
  43. euporie/core/path.py +273 -0
  44. euporie/core/processors.py +35 -0
  45. euporie/core/renderer.py +21 -5
  46. euporie/core/style.py +34 -19
  47. euporie/core/tabs/base.py +101 -17
  48. euporie/core/tabs/notebook.py +72 -30
  49. euporie/core/terminal.py +56 -48
  50. euporie/core/utils.py +12 -16
  51. euporie/core/widgets/cell.py +6 -5
  52. euporie/core/widgets/cell_outputs.py +2 -2
  53. euporie/core/widgets/decor.py +74 -82
  54. euporie/core/widgets/dialog.py +132 -28
  55. euporie/core/widgets/display.py +76 -24
  56. euporie/core/widgets/file_browser.py +87 -31
  57. euporie/core/widgets/formatted_text_area.py +1 -3
  58. euporie/core/widgets/forms.py +79 -40
  59. euporie/core/widgets/inputs.py +23 -13
  60. euporie/core/widgets/layout.py +4 -3
  61. euporie/core/widgets/menu.py +368 -216
  62. euporie/core/widgets/page.py +99 -58
  63. euporie/core/widgets/pager.py +1 -1
  64. euporie/core/widgets/palette.py +30 -27
  65. euporie/core/widgets/search_bar.py +38 -25
  66. euporie/core/widgets/status_bar.py +103 -5
  67. euporie/data/desktop/euporie-console.desktop +7 -0
  68. euporie/data/desktop/euporie-notebook.desktop +7 -0
  69. euporie/hub/__main__.py +3 -1
  70. euporie/hub/app.py +9 -7
  71. euporie/notebook/__main__.py +3 -1
  72. euporie/notebook/app.py +7 -30
  73. euporie/notebook/tabs/__init__.py +7 -3
  74. euporie/notebook/tabs/display.py +18 -9
  75. euporie/notebook/tabs/edit.py +106 -23
  76. euporie/notebook/tabs/json.py +73 -0
  77. euporie/notebook/tabs/log.py +18 -8
  78. euporie/notebook/tabs/notebook.py +60 -41
  79. euporie/preview/__main__.py +3 -1
  80. euporie/preview/app.py +2 -1
  81. euporie/preview/tabs/notebook.py +23 -10
  82. euporie/web/tabs/web.py +149 -0
  83. euporie/web/widgets/webview.py +563 -0
  84. euporie-2.4.1.data/data/share/applications/euporie-console.desktop +7 -0
  85. euporie-2.4.1.data/data/share/applications/euporie-notebook.desktop +7 -0
  86. {euporie-2.3.2.dist-info → euporie-2.4.1.dist-info}/METADATA +6 -5
  87. euporie-2.4.1.dist-info/RECORD +129 -0
  88. {euporie-2.3.2.dist-info → euporie-2.4.1.dist-info}/WHEEL +1 -1
  89. euporie/core/url.py +0 -64
  90. euporie-2.3.2.dist-info/RECORD +0 -122
  91. {euporie-2.3.2.dist-info → euporie-2.4.1.dist-info}/entry_points.txt +0 -0
  92. {euporie-2.3.2.dist-info → euporie-2.4.1.dist-info}/licenses/LICENSE +0 -0
@@ -16,16 +16,15 @@ from prompt_toolkit.filters.base import Condition
16
16
  from prompt_toolkit.filters.utils import to_filter
17
17
  from prompt_toolkit.formatted_text.base import to_formatted_text
18
18
  from prompt_toolkit.formatted_text.utils import fragment_list_width, split_lines
19
- from prompt_toolkit.layout.containers import Float, Window
19
+ from prompt_toolkit.layout.containers import ConditionalContainer, Float, VSplit, Window
20
20
  from prompt_toolkit.layout.controls import GetLinePrefixCallable, UIContent, UIControl
21
- from prompt_toolkit.layout.margins import ConditionalMargin
22
21
  from prompt_toolkit.layout.mouse_handlers import MouseHandlers
23
22
  from prompt_toolkit.layout.screen import Char, WritePosition
24
23
  from prompt_toolkit.mouse_events import MouseEvent, MouseEventType
25
24
  from prompt_toolkit.utils import Event
26
25
 
27
26
  from euporie.core.commands import add_cmd
28
- from euporie.core.convert.base import convert, find_route
27
+ from euporie.core.convert.core import convert, find_route
29
28
  from euporie.core.convert.utils import data_pixel_size, pixels_to_cell_size
30
29
  from euporie.core.current import get_app
31
30
  from euporie.core.data_structures import DiInt
@@ -40,21 +39,21 @@ from euporie.core.key_binding.registry import (
40
39
  load_registered_bindings,
41
40
  register_bindings,
42
41
  )
43
- from euporie.core.margins import ScrollbarMargin
42
+ from euporie.core.margins import MarginContainer, ScrollbarMargin
44
43
  from euporie.core.terminal import tmuxify
45
44
  from euporie.core.widgets.page import BoundedWritePosition
46
45
 
47
46
  if TYPE_CHECKING:
47
+ from pathlib import Path
48
48
  from typing import Any, Callable, Iterable
49
49
 
50
50
  from prompt_toolkit.filters import FilterOrBool
51
51
  from prompt_toolkit.formatted_text import StyleAndTextTuples
52
52
  from prompt_toolkit.key_binding import KeyBindingsBase
53
53
  from prompt_toolkit.key_binding.key_bindings import NotImplementedOrNone
54
- from prompt_toolkit.layout.containers import AnyContainer, WindowRenderInfo
54
+ from prompt_toolkit.layout.containers import AnyContainer
55
55
  from prompt_toolkit.layout.dimension import AnyDimension
56
56
  from prompt_toolkit.layout.screen import Screen
57
- from upath import UPath
58
57
 
59
58
 
60
59
  log = logging.getLogger(__name__)
@@ -71,7 +70,7 @@ class DisplayControl(UIControl):
71
70
  self,
72
71
  data: Any,
73
72
  format_: str,
74
- path: UPath | None = None,
73
+ path: Path | None = None,
75
74
  fg_color: str | None = None,
76
75
  bg_color: str | None = None,
77
76
  sizing_func: Callable[[], tuple[int, float]] | None = None,
@@ -102,11 +101,12 @@ class DisplayControl(UIControl):
102
101
  self.key_bindings = load_registered_bindings(
103
102
  "euporie.core.widgets.display.Display"
104
103
  )
104
+ self._cursor_position = Point(x=0, y=0)
105
+ self.dy = 0
105
106
  self.app = get_app()
106
107
 
108
+ self.on_data_changed = Event(self)
107
109
  self.on_cursor_position_changed = Event(self)
108
- self._cursor_position = Point(x=0, y=0)
109
- self.dy = 0
110
110
 
111
111
  self.sizing_func = sizing_func or (lambda: (0, 0))
112
112
  self._max_cols = 0
@@ -133,6 +133,7 @@ class DisplayControl(UIControl):
133
133
  """Set the control's data."""
134
134
  self._data = value
135
135
  self.reset()
136
+ self.on_data_changed.fire()
136
137
 
137
138
  def get_key_bindings(self) -> KeyBindingsBase | None:
138
139
  """Return the control's key bindings."""
@@ -276,6 +277,7 @@ class DisplayControl(UIControl):
276
277
  def get_invalidate_events(self) -> Iterable[Event[object]]:
277
278
  """Return the Window invalidate events."""
278
279
  # Whenever the cursor position changes, the UI has to be updated.
280
+ yield self.on_data_changed
279
281
  yield self.on_cursor_position_changed
280
282
 
281
283
  @property
@@ -293,7 +295,6 @@ class DisplayWindow(Window):
293
295
  """A window sub-class which can scroll left and right."""
294
296
 
295
297
  content: DisplayControl
296
- render_info: WindowRenderInfo
297
298
  vertical_scroll: int
298
299
 
299
300
  def _write_to_screen_at_index(
@@ -388,7 +389,7 @@ class GraphicControl(DisplayControl, metaclass=ABCMeta):
388
389
  self,
389
390
  data: Any,
390
391
  format_: str,
391
- path: UPath | None = None,
392
+ path: Path | None = None,
392
393
  fg_color: str | None = None,
393
394
  bg_color: str | None = None,
394
395
  sizing_func: Callable[[], tuple[int, float]] | None = None,
@@ -632,7 +633,7 @@ class KittyGraphicControl(GraphicControl):
632
633
  self,
633
634
  data: Any,
634
635
  format_: str,
635
- path: UPath | None = None,
636
+ path: Path | None = None,
636
637
  fg_color: str | None = None,
637
638
  bg_color: str | None = None,
638
639
  sizing_func: Callable[[], tuple[int, float]] | None = None,
@@ -957,7 +958,7 @@ class GraphicFloat(Float):
957
958
  target_window: Window,
958
959
  data: Any,
959
960
  format_: str,
960
- path: UPath | None = None,
961
+ path: Path | None = None,
961
962
  fg_color: str | None = None,
962
963
  bg_color: str | None = None,
963
964
  sizing_func: Callable[[], tuple[int, float]] | None = None,
@@ -1053,6 +1054,32 @@ class GraphicFloat(Float):
1053
1054
  self.control.data = value
1054
1055
  self.control.reset()
1055
1056
 
1057
+ @property
1058
+ def format_(self) -> str:
1059
+ """Return the graphic's current data format."""
1060
+ if self.control is not None:
1061
+ return self.control.format_
1062
+ return ""
1063
+
1064
+ @format_.setter
1065
+ def format_(self, value: str) -> None:
1066
+ """Set the graphic float's data format."""
1067
+ if self.control is not None:
1068
+ self.control.format_ = value
1069
+
1070
+ @property
1071
+ def path(self) -> Path | None:
1072
+ """Return the graphic's current data path."""
1073
+ if self.control is not None:
1074
+ return self.control.path
1075
+ return None
1076
+
1077
+ @path.setter
1078
+ def path(self, value: Path | None) -> None:
1079
+ """Set the graphic float's data path."""
1080
+ if self.control is not None:
1081
+ self.control.path = value
1082
+
1056
1083
 
1057
1084
  class Display:
1058
1085
  """Rich output displays.
@@ -1065,7 +1092,7 @@ class Display:
1065
1092
  self,
1066
1093
  data: Any,
1067
1094
  format_: str,
1068
- path: UPath | None = None,
1095
+ path: Path | None = None,
1069
1096
  fg_color: str | None = None,
1070
1097
  bg_color: str | None = None,
1071
1098
  height: AnyDimension = None,
@@ -1138,16 +1165,19 @@ class Display:
1138
1165
  char=" ",
1139
1166
  )
1140
1167
 
1141
- self.window.right_margins = [
1142
- ConditionalMargin(
1143
- ScrollbarMargin(),
1144
- filter=to_filter(scrollbar)
1145
- & (
1146
- ~to_filter(scrollbar_autohide)
1147
- | (to_filter(scrollbar_autohide) & scrollable(self.window))
1168
+ self.container = VSplit(
1169
+ [
1170
+ self.window,
1171
+ ConditionalContainer(
1172
+ MarginContainer(ScrollbarMargin(), target=self.window),
1173
+ filter=to_filter(scrollbar)
1174
+ & (
1175
+ ~to_filter(scrollbar_autohide)
1176
+ | (to_filter(scrollbar_autohide) & scrollable(self.window))
1177
+ ),
1148
1178
  ),
1149
- )
1150
- ]
1179
+ ]
1180
+ )
1151
1181
 
1152
1182
  # Add graphic
1153
1183
  app = get_app()
@@ -1175,6 +1205,28 @@ class Display:
1175
1205
  self.control.data = value
1176
1206
  self.graphic_float.data = value
1177
1207
 
1208
+ @property
1209
+ def format_(self) -> str:
1210
+ """Return the display's current data format."""
1211
+ return self.control.format_
1212
+
1213
+ @format_.setter
1214
+ def format_(self, value: str) -> None:
1215
+ """Set the display container's data format."""
1216
+ self.control.format_ = value
1217
+ self.graphic_float.format_ = value
1218
+
1219
+ @property
1220
+ def path(self) -> Path | None:
1221
+ """Return the display's current data path."""
1222
+ return self.control.path
1223
+
1224
+ @path.setter
1225
+ def path(self, value: Path | None) -> None:
1226
+ """Set the display container's data path."""
1227
+ self.control.path = value
1228
+ self.graphic_float.path = value
1229
+
1178
1230
  def make_sizing_func(
1179
1231
  self, data: Any, format_: str, fg: str | None, bg: str | None
1180
1232
  ) -> Callable[[], tuple[int, float]]:
@@ -1222,7 +1274,7 @@ class Display:
1222
1274
 
1223
1275
  def __pt_container__(self) -> AnyContainer:
1224
1276
  """Return the content of this output."""
1225
- return self.window
1277
+ return self.container
1226
1278
 
1227
1279
  # ################################### Commands ####################################
1228
1280
 
@@ -5,7 +5,6 @@ from __future__ import annotations
5
5
  import logging
6
6
  from typing import TYPE_CHECKING
7
7
 
8
- from prompt_toolkit.application.current import get_app
9
8
  from prompt_toolkit.buffer import Buffer
10
9
  from prompt_toolkit.cache import FastDictCache
11
10
  from prompt_toolkit.completion import PathCompleter
@@ -20,16 +19,19 @@ from prompt_toolkit.layout.containers import (
20
19
  Window,
21
20
  )
22
21
  from prompt_toolkit.layout.controls import UIContent, UIControl
23
- from prompt_toolkit.mouse_events import MouseEvent, MouseEventType
22
+ from prompt_toolkit.layout.screen import WritePosition
23
+ from prompt_toolkit.mouse_events import MouseButton, MouseEvent, MouseEventType
24
24
  from prompt_toolkit.utils import Event
25
25
  from upath import UPath
26
26
 
27
- from euporie.core.border import InnerEigthGrid
28
- from euporie.core.margins import ScrollbarMargin
27
+ from euporie.core.app import get_app
28
+ from euporie.core.border import InsetGrid
29
+ from euporie.core.margins import MarginContainer, ScrollbarMargin
29
30
  from euporie.core.widgets.decor import Border, FocusedStyle
30
31
  from euporie.core.widgets.forms import Button, Text
31
32
 
32
33
  if TYPE_CHECKING:
34
+ from pathlib import Path
33
35
  from typing import Callable
34
36
 
35
37
  from prompt_toolkit.filters.base import FilterOrBool
@@ -39,6 +41,8 @@ if TYPE_CHECKING:
39
41
  from prompt_toolkit.layout.dimension import AnyDimension
40
42
  from upath.core import PT
41
43
 
44
+ from euporie.core.widgets.status_bar import StatusBarFields
45
+
42
46
  log = logging.getLogger(__name__)
43
47
 
44
48
 
@@ -349,7 +353,7 @@ FILE_ICONS = {
349
353
  }
350
354
 
351
355
 
352
- def is_dir(path: str | UPath) -> bool | None:
356
+ def is_dir(path: str | Path) -> bool | None:
353
357
  """Check if a path is a directory."""
354
358
  test_path = UPath(path)
355
359
  try:
@@ -363,22 +367,25 @@ class FileBrowserControl(UIControl):
363
367
 
364
368
  def __init__(
365
369
  self,
366
- path: UPath | None = None,
370
+ path: Path | None = None,
367
371
  on_chdir: Callable[[FileBrowserControl], None] | None = None,
368
372
  on_select: Callable[[FileBrowserControl], None] | None = None,
369
373
  on_open: Callable[[FileBrowserControl], None] | None = None,
374
+ window: Window | None = None,
370
375
  ) -> None:
371
376
  """Initialize a new file browser instance."""
372
377
  self.dir = path or UPath(".")
373
- self.hovered: int = 0
378
+ self.hovered: int | None = None
374
379
  self.selected: int | None = None
375
380
  self._dir_cache: FastDictCache[
376
- tuple[UPath], list[tuple[bool, UPath]]
381
+ tuple[Path], list[tuple[bool, Path]]
377
382
  ] = FastDictCache(get_value=self.load_path, size=1)
378
383
  self.on_select = Event(self, on_select)
379
384
  self.on_chdir = Event(self, on_chdir)
380
385
  self.on_open = Event(self, on_open)
381
386
 
387
+ self.window = window
388
+
382
389
  self.on_chdir.fire()
383
390
 
384
391
  self.key_bindings = kb = KeyBindings()
@@ -412,12 +419,12 @@ class FileBrowserControl(UIControl):
412
419
  return self.open_path()
413
420
 
414
421
  @property
415
- def contents(self) -> list[tuple[bool, UPath]]:
422
+ def contents(self) -> list[tuple[bool, Path]]:
416
423
  """Return the contents of the current folder."""
417
424
  return self._dir_cache[(self.dir,)]
418
425
 
419
426
  @property
420
- def dir(self) -> UPath:
427
+ def dir(self) -> Path:
421
428
  """Return the current folder path."""
422
429
  return self._dir
423
430
 
@@ -435,12 +442,12 @@ class FileBrowserControl(UIControl):
435
442
  log.warning("'%s' is not a directory, not changing directory", value)
436
443
 
437
444
  @property
438
- def path(self) -> UPath:
445
+ def path(self) -> Path:
439
446
  """Return the current selected path."""
440
447
  return self.contents[self.selected or 0][1]
441
448
 
442
449
  @staticmethod
443
- def load_path(path: UPath) -> list[tuple[bool, UPath]]:
450
+ def load_path(path: Path) -> list[tuple[bool, Path]]:
444
451
  """Return the contents of a folder."""
445
452
  paths = [] if path.parent == path else [path / ".."]
446
453
  try:
@@ -489,11 +496,42 @@ class FileBrowserControl(UIControl):
489
496
  def mouse_handler(self, mouse_event: MouseEvent) -> NotImplementedOrNone:
490
497
  """Handle mouse events."""
491
498
  row = mouse_event.position.y
492
- if mouse_event.event_type == MouseEventType.MOUSE_DOWN:
493
- get_app().layout.current_control = self
499
+ app = get_app()
500
+ if (
501
+ mouse_event.button == MouseButton.LEFT
502
+ and mouse_event.event_type == MouseEventType.MOUSE_DOWN
503
+ ):
504
+ app.layout.current_control = self
505
+ app.mouse_limits = None
506
+ self.hovered = None
494
507
  return self.select(row, open_file=True)
495
508
  elif mouse_event.event_type == MouseEventType.MOUSE_MOVE:
496
- return self.hover(row)
509
+ # Mark item as hovered if mouse is over the control
510
+ if (
511
+ self.window is not None
512
+ and (info := self.window.render_info) is not None
513
+ ):
514
+ rowcol_to_yx = info._rowcol_to_yx
515
+ abs_mouse_pos = (
516
+ mouse_event.position.x + info._x_offset,
517
+ mouse_event.position.y + info._y_offset - info.vertical_scroll,
518
+ )
519
+ if abs_mouse_pos == app.mouse_position:
520
+ row_col_vals = rowcol_to_yx.values()
521
+ y_min, x_min = min(row_col_vals)
522
+ y_max, x_max = max(row_col_vals)
523
+ app.mouse_limits = WritePosition(
524
+ xpos=x_min,
525
+ ypos=y_min,
526
+ width=x_max - x_min + 1,
527
+ height=y_max - y_min + 1,
528
+ )
529
+ return self.hover(row)
530
+ else:
531
+ # Clear mouse limits if mouse is outside control
532
+ app.mouse_limits = None
533
+ self.hovered = None
534
+ return None
497
535
 
498
536
  return NotImplemented
499
537
 
@@ -509,12 +547,13 @@ class FileBrowserControl(UIControl):
509
547
  self.open_path()
510
548
  return None
511
549
 
512
- def hover(self, row: int) -> NotImplementedOrNone:
550
+ def hover(self, row: int | None) -> NotImplementedOrNone:
513
551
  """Hover a file in the browser."""
514
- row = min(max(0, row), len(self.contents) - 1)
515
- if self.hovered != row:
516
- self.hovered = row
517
- return None
552
+ if row is not None:
553
+ row = min(max(0, row), len(self.contents) - 1)
554
+ if self.hovered != row:
555
+ self.hovered = row
556
+ return None
518
557
  return NotImplemented
519
558
 
520
559
  def open_path(self) -> None:
@@ -555,6 +594,14 @@ class FileBrowserControl(UIControl):
555
594
  """Determine that the file_browser is focusable."""
556
595
  return True
557
596
 
597
+ def __pt_status__(self) -> StatusBarFields:
598
+ """Show the selected or hovered path in the statusbar."""
599
+ if self.hovered is not None:
600
+ return [[("", str(self.contents[self.hovered][1]))]], []
601
+ elif self.selected is not None:
602
+ return [[("", str(self.contents[self.selected][1]))]], []
603
+ return [], []
604
+
558
605
 
559
606
  class FileBrowser:
560
607
  """A file browser."""
@@ -563,10 +610,10 @@ class FileBrowser:
563
610
 
564
611
  def __init__(
565
612
  self,
566
- path: UPath | None = None,
567
- on_select: Callable[[UPath], None] | None = None,
568
- on_open: Callable[[UPath], None] | None = None,
569
- on_chdir: Callable[[UPath], None] | None = None,
613
+ path: Path | None = None,
614
+ on_select: Callable[[Path], None] | None = None,
615
+ on_open: Callable[[Path], None] | None = None,
616
+ on_chdir: Callable[[Path], None] | None = None,
570
617
  width: AnyDimension = None,
571
618
  height: AnyDimension = None,
572
619
  style: str = "",
@@ -575,7 +622,7 @@ class FileBrowser:
575
622
  """Create a new instance."""
576
623
 
577
624
  def _accept_path(buffer: Buffer) -> bool:
578
- control.dir = UPath(buffer.text)
625
+ control.dir = Path(buffer.text)
579
626
  return True
580
627
 
581
628
  def _validate_path(path: str) -> bool:
@@ -588,7 +635,6 @@ class FileBrowser:
588
635
  )
589
636
  self.control = control = FileBrowserControl(
590
637
  path=path,
591
- on_open=lambda x: log.debug(x.path),
592
638
  on_chdir=lambda x: setattr(text, "text", str(x.dir)),
593
639
  )
594
640
  if on_select is not None:
@@ -622,12 +668,16 @@ class FileBrowser:
622
668
  ),
623
669
  FocusedStyle(
624
670
  Border(
625
- Window(
626
- control,
627
- style="class:face",
628
- right_margins=[ScrollbarMargin()],
671
+ VSplit(
672
+ [
673
+ window := Window(
674
+ control,
675
+ style="class:face",
676
+ ),
677
+ MarginContainer(ScrollbarMargin(), target=window),
678
+ ]
629
679
  ),
630
- border=InnerEigthGrid,
680
+ border=InsetGrid,
631
681
  style="class:input,inset,border",
632
682
  ),
633
683
  style_hover="",
@@ -637,7 +687,13 @@ class FileBrowser:
637
687
  width=width,
638
688
  height=height,
639
689
  )
690
+ # Set control's window so it can determine it's position for mouse-over
691
+ control.window = window
640
692
 
641
693
  def __pt_container__(self) -> AnyContainer:
642
694
  """Return the tree-view container's content."""
643
695
  return self.container
696
+
697
+ def __pt_status__(self) -> StatusBarFields:
698
+ """Show the selected or hovered path in the statusbar."""
699
+ return self.control.__pt_status__()
@@ -15,7 +15,7 @@ from prompt_toolkit.layout.margins import ConditionalMargin
15
15
  from prompt_toolkit.layout.processors import DynamicProcessor, Processor, Transformation
16
16
  from prompt_toolkit.widgets import TextArea
17
17
 
18
- from euporie.core.margins import NumberedDiffMargin, ScrollbarMargin
18
+ from euporie.core.margins import NumberedDiffMargin
19
19
 
20
20
  if TYPE_CHECKING:
21
21
  from typing import Any
@@ -98,8 +98,6 @@ class FormattedTextArea(TextArea):
98
98
  self.line_numbers,
99
99
  )
100
100
  ]
101
- # Add auto scrollbar
102
- self.window.right_margins = [ScrollbarMargin(display_arrows=False)]
103
101
  # Set the formatted text to display
104
102
  self._set_formatted_text(formatted_text)
105
103