euporie 2.8.1__py3-none-any.whl → 2.8.5__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 (129) hide show
  1. euporie/console/_commands.py +143 -0
  2. euporie/console/_settings.py +58 -0
  3. euporie/console/app.py +25 -71
  4. euporie/console/tabs/console.py +267 -147
  5. euporie/core/__init__.py +1 -9
  6. euporie/core/__main__.py +31 -5
  7. euporie/core/_settings.py +104 -0
  8. euporie/core/app/__init__.py +3 -0
  9. euporie/core/app/_commands.py +70 -0
  10. euporie/core/app/_settings.py +427 -0
  11. euporie/core/{app.py → app/app.py} +214 -572
  12. euporie/core/app/base.py +51 -0
  13. euporie/core/{current.py → app/current.py} +13 -4
  14. euporie/core/app/cursor.py +35 -0
  15. euporie/core/app/dummy.py +12 -0
  16. euporie/core/app/launch.py +28 -0
  17. euporie/core/bars/__init__.py +11 -0
  18. euporie/core/bars/command.py +182 -0
  19. euporie/core/bars/menu.py +258 -0
  20. euporie/core/{widgets → bars}/search.py +154 -57
  21. euporie/core/{widgets → bars}/status.py +9 -26
  22. euporie/core/clipboard.py +19 -80
  23. euporie/core/comm/base.py +8 -6
  24. euporie/core/comm/ipywidgets.py +21 -12
  25. euporie/core/comm/registry.py +2 -1
  26. euporie/core/commands.py +11 -5
  27. euporie/core/completion.py +3 -2
  28. euporie/core/config.py +368 -341
  29. euporie/core/convert/__init__.py +0 -30
  30. euporie/core/convert/datum.py +131 -60
  31. euporie/core/convert/formats/__init__.py +31 -0
  32. euporie/core/convert/formats/ansi.py +46 -30
  33. euporie/core/convert/formats/common.py +11 -23
  34. euporie/core/convert/formats/html.py +45 -40
  35. euporie/core/convert/formats/pil.py +1 -1
  36. euporie/core/convert/formats/png.py +3 -5
  37. euporie/core/convert/formats/sixel.py +3 -3
  38. euporie/core/convert/registry.py +11 -8
  39. euporie/core/convert/utils.py +50 -23
  40. euporie/core/diagnostics.py +2 -2
  41. euporie/core/filters.py +72 -82
  42. euporie/core/format.py +13 -2
  43. euporie/core/ft/ansi.py +1 -1
  44. euporie/core/ft/html.py +36 -36
  45. euporie/core/ft/table.py +1 -3
  46. euporie/core/ft/utils.py +4 -1
  47. euporie/core/graphics.py +216 -124
  48. euporie/core/history.py +2 -2
  49. euporie/core/inspection.py +3 -2
  50. euporie/core/io.py +207 -28
  51. euporie/core/kernel/__init__.py +1 -0
  52. euporie/core/{kernel.py → kernel/client.py} +100 -139
  53. euporie/core/kernel/manager.py +114 -0
  54. euporie/core/key_binding/bindings/__init__.py +2 -8
  55. euporie/core/key_binding/bindings/basic.py +47 -7
  56. euporie/core/key_binding/bindings/completion.py +3 -8
  57. euporie/core/key_binding/bindings/micro.py +5 -7
  58. euporie/core/key_binding/bindings/mouse.py +26 -24
  59. euporie/core/key_binding/bindings/terminal.py +193 -0
  60. euporie/core/key_binding/bindings/vi.py +46 -0
  61. euporie/core/key_binding/key_processor.py +43 -2
  62. euporie/core/key_binding/registry.py +2 -0
  63. euporie/core/key_binding/utils.py +22 -2
  64. euporie/core/keys.py +7156 -93
  65. euporie/core/layout/cache.py +35 -25
  66. euporie/core/layout/containers.py +280 -74
  67. euporie/core/layout/decor.py +5 -5
  68. euporie/core/layout/mouse.py +1 -1
  69. euporie/core/layout/print.py +16 -3
  70. euporie/core/layout/scroll.py +26 -28
  71. euporie/core/log.py +75 -60
  72. euporie/core/lsp.py +118 -24
  73. euporie/core/margins.py +60 -31
  74. euporie/core/path.py +2 -1
  75. euporie/core/renderer.py +58 -17
  76. euporie/core/style.py +60 -40
  77. euporie/core/suggest.py +103 -85
  78. euporie/core/tabs/__init__.py +34 -0
  79. euporie/core/tabs/_settings.py +113 -0
  80. euporie/core/tabs/base.py +11 -435
  81. euporie/core/tabs/kernel.py +420 -0
  82. euporie/core/tabs/notebook.py +20 -54
  83. euporie/core/utils.py +98 -6
  84. euporie/core/validation.py +1 -1
  85. euporie/core/widgets/_settings.py +188 -0
  86. euporie/core/widgets/cell.py +90 -158
  87. euporie/core/widgets/cell_outputs.py +25 -36
  88. euporie/core/widgets/decor.py +11 -41
  89. euporie/core/widgets/dialog.py +55 -44
  90. euporie/core/widgets/display.py +27 -24
  91. euporie/core/widgets/file_browser.py +5 -26
  92. euporie/core/widgets/forms.py +16 -12
  93. euporie/core/widgets/inputs.py +37 -81
  94. euporie/core/widgets/layout.py +7 -6
  95. euporie/core/widgets/logo.py +49 -0
  96. euporie/core/widgets/menu.py +13 -11
  97. euporie/core/widgets/pager.py +8 -11
  98. euporie/core/widgets/palette.py +6 -6
  99. euporie/hub/app.py +52 -31
  100. euporie/notebook/_commands.py +24 -0
  101. euporie/notebook/_settings.py +107 -0
  102. euporie/notebook/app.py +109 -210
  103. euporie/notebook/filters.py +1 -1
  104. euporie/notebook/tabs/__init__.py +46 -7
  105. euporie/notebook/tabs/_commands.py +714 -0
  106. euporie/notebook/tabs/_settings.py +32 -0
  107. euporie/notebook/tabs/display.py +2 -2
  108. euporie/notebook/tabs/edit.py +12 -7
  109. euporie/notebook/tabs/json.py +3 -3
  110. euporie/notebook/tabs/log.py +1 -18
  111. euporie/notebook/tabs/notebook.py +21 -674
  112. euporie/notebook/widgets/_commands.py +11 -0
  113. euporie/notebook/widgets/_settings.py +19 -0
  114. euporie/notebook/widgets/side_bar.py +14 -34
  115. euporie/preview/_settings.py +104 -0
  116. euporie/preview/app.py +8 -30
  117. euporie/preview/tabs/notebook.py +15 -86
  118. euporie/web/tabs/web.py +4 -6
  119. euporie/web/widgets/webview.py +5 -12
  120. {euporie-2.8.1.dist-info → euporie-2.8.5.dist-info}/METADATA +11 -15
  121. euporie-2.8.5.dist-info/RECORD +172 -0
  122. {euporie-2.8.1.dist-info → euporie-2.8.5.dist-info}/WHEEL +1 -1
  123. {euporie-2.8.1.dist-info → euporie-2.8.5.dist-info}/entry_points.txt +2 -2
  124. {euporie-2.8.1.dist-info → euporie-2.8.5.dist-info}/licenses/LICENSE +1 -1
  125. euporie/core/launch.py +0 -59
  126. euporie/core/terminal.py +0 -527
  127. euporie-2.8.1.dist-info/RECORD +0 -146
  128. {euporie-2.8.1.data → euporie-2.8.5.data}/data/share/applications/euporie-console.desktop +0 -0
  129. {euporie-2.8.1.data → euporie-2.8.5.data}/data/share/applications/euporie-notebook.desktop +0 -0
@@ -20,9 +20,8 @@ from prompt_toolkit.mouse_events import MouseButton, MouseEvent, MouseEventType
20
20
  from prompt_toolkit.utils import Event
21
21
  from upath import UPath
22
22
 
23
- from euporie.core.app import get_app
23
+ from euporie.core.app.current import get_app
24
24
  from euporie.core.border import InsetGrid
25
- from euporie.core.config import add_setting
26
25
  from euporie.core.layout.containers import HSplit, VSplit, Window
27
26
  from euporie.core.layout.decor import FocusedStyle
28
27
  from euporie.core.margins import MarginContainer, ScrollbarMargin
@@ -41,7 +40,7 @@ if TYPE_CHECKING:
41
40
  from prompt_toolkit.layout.dimension import AnyDimension
42
41
  from upath.core import PT
43
42
 
44
- from euporie.core.widgets.status import StatusBarFields
43
+ from euporie.core.bars.status import StatusBarFields
45
44
 
46
45
  log = logging.getLogger(__name__)
47
46
 
@@ -377,9 +376,9 @@ class FileBrowserControl(UIControl):
377
376
  self.dir = path or UPath(".")
378
377
  self.hovered: int | None = None
379
378
  self.selected: int | None = None
380
- self._dir_cache: FastDictCache[
381
- tuple[Path], list[tuple[bool, Path]]
382
- ] = FastDictCache(get_value=self.load_path, size=1)
379
+ self._dir_cache: FastDictCache[tuple[Path], list[tuple[bool, Path]]] = (
380
+ FastDictCache(get_value=self.load_path, size=1)
381
+ )
383
382
  self.on_select = Event(self, on_select)
384
383
  self.on_chdir = Event(self, on_chdir)
385
384
  self.on_open = Event(self, on_open)
@@ -704,23 +703,3 @@ class FileBrowser:
704
703
  def __pt_status__(self) -> StatusBarFields:
705
704
  """Show the selected or hovered path in the statusbar."""
706
705
  return self.control.__pt_status__()
707
-
708
- # ################################### Settings ####################################
709
-
710
- add_setting(
711
- name="show_file_icons",
712
- flags=["--show-file-icons"],
713
- type_=bool,
714
- title="File icons",
715
- help_="Show file icons in the file manager",
716
- default=False,
717
- schema={
718
- "type": "boolean",
719
- },
720
- description="""
721
- Whether file icons should be shown in the file manager.
722
-
723
- These icons exist in the unicode private use area, and may require custom
724
- fonts such as ``awesome-terminal-fonts`` or ``nerdfonts`` to be installed.
725
- """,
726
- )
@@ -8,7 +8,7 @@ from abc import ABCMeta, abstractmethod
8
8
  from collections import deque
9
9
  from functools import partial
10
10
  from math import ceil, floor
11
- from typing import TYPE_CHECKING, Dict, cast
11
+ from typing import TYPE_CHECKING, cast
12
12
  from weakref import finalize
13
13
 
14
14
  from prompt_toolkit.buffer import ValidationState
@@ -47,8 +47,8 @@ from prompt_toolkit.utils import Event
47
47
  from prompt_toolkit.validation import Validator
48
48
  from prompt_toolkit.widgets.base import TextArea
49
49
 
50
+ from euporie.core.app.current import get_app
50
51
  from euporie.core.border import InsetGrid
51
- from euporie.core.current import get_app
52
52
  from euporie.core.data_structures import DiBool
53
53
  from euporie.core.ft.utils import FormattedTextAlign, align
54
54
  from euporie.core.margins import MarginContainer, ScrollbarMargin
@@ -56,7 +56,8 @@ from euporie.core.widgets.decor import Border, Shadow
56
56
  from euporie.core.widgets.layout import Box, ConditionalSplit
57
57
 
58
58
  if TYPE_CHECKING:
59
- from typing import Any, Callable, Sequence
59
+ from collections.abc import Sequence
60
+ from typing import Any, Callable
60
61
 
61
62
  from prompt_toolkit.buffer import Buffer, BufferAcceptHandler
62
63
  from prompt_toolkit.completion.base import Completer
@@ -236,6 +237,7 @@ class Button:
236
237
 
237
238
  def default_mouse_handler(self, mouse_event: MouseEvent) -> NotImplementedOrNone:
238
239
  """Handle mouse events."""
240
+ app = get_app()
239
241
  if self.disabled():
240
242
  return NotImplemented
241
243
  if mouse_event.button == MouseButton.LEFT:
@@ -249,16 +251,15 @@ class Button:
249
251
  ):
250
252
  y_min, x_min = min(render_info._rowcol_to_yx.values())
251
253
  y_max, x_max = max(render_info._rowcol_to_yx.values())
252
- get_app().mouse_limits = WritePosition(
254
+ app.mouse_limits = WritePosition(
253
255
  xpos=x_min,
254
256
  ypos=y_min,
255
- width=x_max - x_min,
256
- height=y_max - y_min,
257
+ width=x_max - x_min + 1,
258
+ height=y_max - y_min + 1,
257
259
  )
258
260
  self.on_mouse_down.fire()
259
261
 
260
262
  if not self.has_focus():
261
- app = get_app()
262
263
  app.layout.focus(self)
263
264
  # Invalidate the app - we don't do it by returning None so the
264
265
  # event can bubble
@@ -269,7 +270,7 @@ class Button:
269
270
  return None
270
271
 
271
272
  elif mouse_event.event_type == MouseEventType.MOUSE_UP:
272
- get_app().mouse_limits = None
273
+ app.mouse_limits = None
273
274
  if self.selected:
274
275
  self.selected = False
275
276
  self.on_click.fire()
@@ -280,11 +281,12 @@ class Button:
280
281
  if (info := self.window.render_info) is not None and (
281
282
  info._x_offset + mouse_event.position.x,
282
283
  info._y_offset + mouse_event.position.y,
283
- ) != get_app().mouse_position:
284
+ ) != app.mouse_position:
285
+ app.mouse_limits = None
284
286
  self.selected = False
285
287
  return None
286
288
 
287
- get_app().mouse_limits = None
289
+ app.mouse_limits = None
288
290
  self.selected = False
289
291
  return NotImplemented
290
292
 
@@ -727,9 +729,11 @@ class Label:
727
729
  value = self.value
728
730
  data = value() if callable(value) else value
729
731
  if self.html():
732
+ from prompt_toolkit.formatted_text.utils import to_plain_text
733
+
730
734
  from euporie.core.ft.html import HTML
731
735
 
732
- return HTML(data, collapse_root_margin=True, fill=False)
736
+ return HTML(to_plain_text(data), collapse_root_margin=True, fill=False)
733
737
  return data
734
738
 
735
739
  def __pt_container__(self) -> AnyContainer:
@@ -955,7 +959,7 @@ class Progress:
955
959
  return self.container
956
960
 
957
961
 
958
- class SizedMask(Dict[int, bool]):
962
+ class SizedMask(dict[int, bool]):
959
963
  """Mask with restricted number of True items."""
960
964
 
961
965
  def __init__(self, size: int | None = None) -> None:
@@ -3,6 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import logging
6
+ from functools import lru_cache
6
7
  from typing import TYPE_CHECKING
7
8
 
8
9
  from prompt_toolkit.auto_suggest import AutoSuggest, DynamicAutoSuggest
@@ -48,9 +49,8 @@ from prompt_toolkit.widgets import TextArea
48
49
  from prompt_toolkit.widgets.toolbars import SearchToolbar
49
50
  from pygments.lexers import ClassNotFound, get_lexer_by_name
50
51
 
52
+ from euporie.core.app.current import get_app
51
53
  from euporie.core.commands import add_cmd
52
- from euporie.core.config import add_setting
53
- from euporie.core.current import get_app
54
54
  from euporie.core.diagnostics import Report
55
55
  from euporie.core.filters import buffer_is_code, scrollable
56
56
  from euporie.core.key_binding.registry import (
@@ -70,10 +70,10 @@ from euporie.core.processors import (
70
70
  ShowTrailingWhiteSpaceProcessor,
71
71
  )
72
72
  from euporie.core.suggest import ConditionalAutoSuggestAsync
73
- from euporie.core.widgets.pager import PagerState
74
73
 
75
74
  if TYPE_CHECKING:
76
- from typing import Callable, Sequence
75
+ from collections.abc import Sequence
76
+ from typing import Callable
77
77
 
78
78
  from prompt_toolkit.buffer import BufferAcceptHandler
79
79
  from prompt_toolkit.filters import Filter, FilterOrBool
@@ -88,15 +88,30 @@ if TYPE_CHECKING:
88
88
  from prompt_toolkit.layout.margins import Margin
89
89
  from prompt_toolkit.lexers.base import Lexer
90
90
 
91
+ from euporie.core.bars.status import StatusBarFields
91
92
  from euporie.core.format import Formatter
92
93
  from euporie.core.inspection import Inspector
93
- from euporie.core.tabs.base import KernelTab
94
- from euporie.core.widgets.status import StatusBarFields
94
+ from euporie.core.tabs.kernel import KernelTab
95
95
 
96
96
 
97
97
  log = logging.getLogger(__name__)
98
98
 
99
99
 
100
+ @lru_cache
101
+ def _get_lexer(highlight: bool, lexer: Lexer | None, language: str) -> Lexer:
102
+ """Determine which lexer should be used for syntax highlighting."""
103
+ if not highlight:
104
+ return SimpleLexer()
105
+ elif lexer is not None:
106
+ return lexer
107
+ try:
108
+ pygments_lexer_class = get_lexer_by_name(language).__class__
109
+ except ClassNotFound:
110
+ return SimpleLexer()
111
+ else:
112
+ return PygmentsLexer(pygments_lexer_class, sync_from_start=False)
113
+
114
+
100
115
  class KernelInput(TextArea):
101
116
  """Kernel input text areas.
102
117
 
@@ -129,7 +144,7 @@ class KernelInput(TextArea):
129
144
  scrollbar: FilterOrBool = True,
130
145
  style: str = "class:kernel-input",
131
146
  search_field: SearchToolbar | None = None,
132
- preview_search: FilterOrBool = True,
147
+ preview_search: FilterOrBool = False,
133
148
  prompt: AnyFormattedText = "",
134
149
  input_processors: list[Processor] | None = None,
135
150
  name: str = "",
@@ -182,14 +197,6 @@ class KernelInput(TextArea):
182
197
  self._language = language
183
198
  self.lexer = lexer
184
199
 
185
- def _get_lexer() -> Lexer:
186
- try:
187
- pygments_lexer_class = get_lexer_by_name(self.language).__class__
188
- except ClassNotFound:
189
- return SimpleLexer()
190
- else:
191
- return PygmentsLexer(pygments_lexer_class, sync_from_start=False)
192
-
193
200
  self.formatters = formatters if formatters is not None else []
194
201
  self._diagnostics = diagnostics or Report()
195
202
  self.inspector = inspector
@@ -228,7 +235,8 @@ class KernelInput(TextArea):
228
235
 
229
236
  # Set extra key-bindings
230
237
  widgets_key_bindings = load_registered_bindings(
231
- "euporie.core.widgets.inputs.KernelInput"
238
+ "euporie.core.widgets.inputs.KernelInput",
239
+ config=app.config,
232
240
  )
233
241
  if key_bindings:
234
242
  widgets_key_bindings = merge_key_bindings(
@@ -240,7 +248,11 @@ class KernelInput(TextArea):
240
248
 
241
249
  self.control = BufferControl(
242
250
  buffer=self.buffer,
243
- lexer=DynamicLexer(lambda: self.lexer or _get_lexer()),
251
+ lexer=DynamicLexer(
252
+ lambda: _get_lexer(
253
+ app.config.syntax_highlighting, self.lexer, self.language
254
+ )
255
+ ),
244
256
  input_processors=[
245
257
  ConditionalProcessor(
246
258
  DiagnosticProcessor(_get_diagnostics),
@@ -283,7 +295,7 @@ class KernelInput(TextArea):
283
295
  diagnostics=_get_diagnostics,
284
296
  show_diagnostics=to_filter(show_diagnostics),
285
297
  ),
286
- app.config.filter("line_numbers") & self.buffer.multiline,
298
+ app.config.filters.line_numbers & self.buffer.multiline,
287
299
  ),
288
300
  ]
289
301
  right_margins = [OverflowMargin()]
@@ -353,6 +365,8 @@ class KernelInput(TextArea):
353
365
 
354
366
  async def inspect(self, auto: bool = False) -> None:
355
367
  """Get contextual help for the current cursor position in the current cell."""
368
+ from euporie.core.widgets.pager import PagerState
369
+
356
370
  if self.inspector is None:
357
371
  return
358
372
 
@@ -414,64 +428,6 @@ class KernelInput(TextArea):
414
428
  """Return the widget's container."""
415
429
  return self.container
416
430
 
417
- # ################################### Settings ####################################
418
-
419
- add_setting(
420
- name="line_numbers",
421
- flags=["--line-numbers"],
422
- type_=bool,
423
- help_="Show or hide line numbers",
424
- default=True,
425
- description="""
426
- Whether line numbers are shown by default.
427
- """,
428
- hooks=[lambda x: get_app().refresh()],
429
- )
430
-
431
- add_setting(
432
- name="autoformat",
433
- flags=["--autoformat"],
434
- type_=bool,
435
- help_="Automatically re-format code cells when run",
436
- default=False,
437
- description="""
438
- Whether to automatically reformat code cells before they are run.
439
- """,
440
- )
441
-
442
- add_setting(
443
- name="autocomplete",
444
- flags=["--autocomplete"],
445
- type_=bool,
446
- help_="Provide completions suggestions automatically",
447
- default=False,
448
- description="""
449
- Whether to automatically suggestion completions while typing in code cells.
450
- """,
451
- )
452
-
453
- add_setting(
454
- name="autosuggest",
455
- flags=["--autosuggest"],
456
- type_=bool,
457
- help_="Provide line completion suggestions",
458
- default=True,
459
- description="""
460
- Whether to automatically suggestion line content while typing in code cells.
461
- """,
462
- )
463
-
464
- add_setting(
465
- name="autoinspect",
466
- flags=["--autoinspect"],
467
- type_=bool,
468
- help_="Display contextual help automatically",
469
- default=False,
470
- description="""
471
- Whether to automatically display contextual help when navigating through code cells.
472
- """,
473
- )
474
-
475
431
  # ################################### Commands ####################################
476
432
 
477
433
  @staticmethod
@@ -480,7 +436,7 @@ class KernelInput(TextArea):
480
436
  )
481
437
  async def _show_contextual_help() -> None:
482
438
  """Display contextual help."""
483
- from euporie.core.tabs.base import KernelTab
439
+ from euporie.core.tabs.kernel import KernelTab
484
440
 
485
441
  tab = get_app().tab
486
442
  if isinstance(tab, KernelTab) and (input_box := tab.current_input) is not None:
@@ -490,7 +446,7 @@ class KernelInput(TextArea):
490
446
  @add_cmd(filter=buffer_is_code & buffer_has_focus)
491
447
  def _history_prev() -> None:
492
448
  """Get the previous history entry."""
493
- from euporie.core.app import get_app
449
+ from euporie.core.app.current import get_app
494
450
 
495
451
  get_app().current_buffer.history_backward()
496
452
 
@@ -498,7 +454,7 @@ class KernelInput(TextArea):
498
454
  @add_cmd(filter=buffer_is_code & buffer_has_focus)
499
455
  def _history_next() -> None:
500
456
  """Get the next history entry."""
501
- from euporie.core.app import get_app
457
+ from euporie.core.app.current import get_app
502
458
 
503
459
  get_app().current_buffer.history_forward()
504
460
 
@@ -506,8 +462,8 @@ class KernelInput(TextArea):
506
462
  @add_cmd()
507
463
  def _reformat_input() -> None:
508
464
  """Format the contents of the current input field."""
509
- from euporie.core.app import get_app
510
- from euporie.core.tabs.base import KernelTab
465
+ from euporie.core.app.current import get_app
466
+ from euporie.core.tabs.kernel import KernelTab
511
467
 
512
468
  if (
513
469
  (tab := get_app().tab)
@@ -35,12 +35,13 @@ from euporie.core.layout.containers import HSplit, VSplit, Window
35
35
  from euporie.core.widgets.decor import Border
36
36
 
37
37
  if TYPE_CHECKING:
38
- from typing import Any, Callable, Sequence
38
+ from collections.abc import Sequence
39
+ from typing import Any, Callable
39
40
 
40
41
  from prompt_toolkit.filters import FilterOrBool
41
42
  from prompt_toolkit.formatted_text.base import AnyFormattedText, StyleAndTextTuples
42
43
  from prompt_toolkit.key_binding.key_bindings import (
43
- KeyBindings,
44
+ KeyBindingsBase,
44
45
  NotImplementedOrNone,
45
46
  )
46
47
  from prompt_toolkit.layout.containers import AnyContainer, Container, _Split
@@ -84,7 +85,7 @@ class Box:
84
85
  style: str = "",
85
86
  char: None | str | Callable[[], str] = None,
86
87
  modal: bool = False,
87
- key_bindings: KeyBindings | None = None,
88
+ key_bindings: KeyBindingsBase | None = None,
88
89
  ) -> None:
89
90
  """Initialize this widget."""
90
91
  if padding is None:
@@ -117,7 +118,7 @@ class Box:
117
118
  height=height,
118
119
  style=style,
119
120
  modal=modal,
120
- key_bindings=None,
121
+ key_bindings=key_bindings,
121
122
  )
122
123
 
123
124
  def __pt_container__(self) -> Container:
@@ -155,7 +156,7 @@ class ConditionalSplit:
155
156
  return self._cache.get(vertical, partial(self.load_container, vertical))
156
157
 
157
158
  def __pt_container__(self) -> AnyContainer:
158
- """Return a dymanic container."""
159
+ """Return a dynamic container."""
159
160
  return DynamicContainer(self.container)
160
161
 
161
162
 
@@ -287,7 +288,7 @@ class TabBarControl(UIControl):
287
288
 
288
289
  def is_focusable(self) -> bool:
289
290
  """Tell whether this user control is focusable."""
290
- return True
291
+ return False
291
292
 
292
293
  def create_content(self, width: int, height: int) -> UIContent:
293
294
  """Generate the formatted text fragments which make the controls output."""
@@ -0,0 +1,49 @@
1
+ """Defines a logo widget."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING, Callable
6
+
7
+ from prompt_toolkit.layout.containers import WindowAlign
8
+ from prompt_toolkit.layout.controls import FormattedTextControl
9
+
10
+ from euporie.core import __logo__
11
+ from euporie.core.filters import has_tabs
12
+ from euporie.core.layout.containers import Window
13
+
14
+ if TYPE_CHECKING:
15
+ from typing import Any
16
+
17
+ from prompt_toolkit.filters import FilterOrBool
18
+ from prompt_toolkit.layout.controls import UIControl
19
+ from prompt_toolkit.layout.dimension import AnyDimension
20
+
21
+
22
+ class Logo(Window):
23
+ """A widget to display the application's logo."""
24
+
25
+ def __init__(
26
+ self,
27
+ content: UIControl | None = None,
28
+ height: AnyDimension = 1,
29
+ width: AnyDimension = 3,
30
+ style: str | Callable[[], str] = "class:menu,logo",
31
+ dont_extend_width: FilterOrBool = True,
32
+ align: WindowAlign | Callable[[], WindowAlign] = WindowAlign.CENTER,
33
+ **kwargs: Any,
34
+ ) -> None:
35
+ """Create a new window with defaults specified."""
36
+ if content is None:
37
+ content = FormattedTextControl(
38
+ [("", f" {__logo__} ")],
39
+ focusable=~has_tabs,
40
+ show_cursor=False,
41
+ )
42
+ super().__init__(
43
+ content=content,
44
+ height=height,
45
+ width=width,
46
+ style=style,
47
+ dont_extend_width=dont_extend_width,
48
+ **kwargs,
49
+ )
@@ -37,14 +37,15 @@ from prompt_toolkit.layout.utils import explode_text_fragments
37
37
  from prompt_toolkit.mouse_events import MouseEvent, MouseEventType
38
38
  from prompt_toolkit.utils import get_cwidth
39
39
 
40
+ from euporie.core.app.current import get_app
41
+ from euporie.core.bars.status import StatusContainer
40
42
  from euporie.core.border import OuterHalfGrid
41
- from euporie.core.current import get_app
42
43
  from euporie.core.layout.containers import HSplit, VSplit, Window
43
44
  from euporie.core.widgets.decor import Shadow
44
- from euporie.core.widgets.status import StatusContainer
45
45
 
46
46
  if TYPE_CHECKING:
47
- from typing import Any, Callable, Iterable, Sequence
47
+ from collections.abc import Iterable, Sequence
48
+ from typing import Any, Callable
48
49
 
49
50
  from prompt_toolkit.filters import Filter, FilterOrBool
50
51
  from prompt_toolkit.formatted_text.base import (
@@ -57,10 +58,10 @@ if TYPE_CHECKING:
57
58
  from prompt_toolkit.layout.containers import AnyContainer
58
59
  from prompt_toolkit.layout.controls import UIControl
59
60
 
60
- from euporie.core.app import BaseApp
61
+ from euporie.core.app.app import BaseApp
62
+ from euporie.core.bars.status import StatusBarFields
61
63
  from euporie.core.border import GridStyle
62
64
  from euporie.core.commands import Command
63
- from euporie.core.widgets.status import StatusBarFields
64
65
 
65
66
 
66
67
  log = logging.getLogger(__name__)
@@ -326,7 +327,6 @@ class MenuBar:
326
327
  def _cancel(event: KeyPressEvent) -> None:
327
328
  """Leave menu."""
328
329
  self.selected_menu = []
329
- log.debug(self.last_focused)
330
330
  self.refocus()
331
331
 
332
332
  # Sub menu navigation.
@@ -408,8 +408,6 @@ class MenuBar:
408
408
  """Close the current menu."""
409
409
  if self.selected_menu:
410
410
  self.selected_menu = self.selected_menu[:-1]
411
- log.debug(self.selected_menu)
412
- log.debug(self.last_focused)
413
411
  self.refocus()
414
412
 
415
413
  # Add global CUA menu shortcut
@@ -516,20 +514,24 @@ class MenuBar:
516
514
  focused = self.focused()
517
515
 
518
516
  # This is called during the rendering. When we discover that this
519
- # widget doesn't have the focus anymore. Reset menu state.
517
+ # widget doesn't have the focus anymore, reset the menu state.
520
518
  if not focused:
521
519
  self.selected_menu = []
522
520
 
523
521
  def mouse_handler(index: int, mouse_event: MouseEvent) -> NotImplementedOrNone:
524
522
  focused = self.focused()
525
523
  hover = mouse_event.event_type == MouseEventType.MOUSE_MOVE
526
- if mouse_event.event_type == MouseEventType.MOUSE_DOWN or hover and focused:
524
+ if mouse_event.event_type == MouseEventType.MOUSE_DOWN or (
525
+ hover and focused
526
+ ):
527
527
  # Toggle focus.
528
528
  if not hover and focused and self.selected_menu == [index]:
529
529
  self.selected_menu = []
530
- self.selected_menu = [index]
530
+ else:
531
+ self.selected_menu = [index]
531
532
  self.refocus()
532
533
  return None
534
+
533
535
  return NotImplemented
534
536
 
535
537
  results: StyleAndTextTuples = []
@@ -9,16 +9,12 @@ from typing import TYPE_CHECKING, NamedTuple
9
9
  from prompt_toolkit.filters import Condition
10
10
  from prompt_toolkit.layout.containers import (
11
11
  ConditionalContainer,
12
- DynamicContainer,
13
12
  )
14
13
  from prompt_toolkit.layout.dimension import Dimension
15
14
 
15
+ from euporie.core.app.current import get_app
16
16
  from euporie.core.commands import add_cmd
17
- from euporie.core.convert.datum import Datum
18
- from euporie.core.convert.formats import BASE64_FORMATS
19
- from euporie.core.convert.mime import MIME_FORMATS
20
17
  from euporie.core.convert.registry import find_route
21
- from euporie.core.current import get_app
22
18
  from euporie.core.filters import pager_has_focus
23
19
  from euporie.core.key_binding.registry import (
24
20
  load_registered_bindings,
@@ -67,6 +63,10 @@ class PagerOutputDataElement(CellOutputDataElement):
67
63
  metadata: Any metadata relating to the data
68
64
  parent: The parent container the output-element is attached to
69
65
  """
66
+ from euporie.core.convert.datum import Datum
67
+ from euporie.core.convert.formats import BASE64_FORMATS
68
+ from euporie.core.convert.mime import MIME_FORMATS
69
+
70
70
  # Get internal format
71
71
  format_ = "ansi"
72
72
  mime_path = PurePath(mime)
@@ -139,15 +139,12 @@ class Pager:
139
139
  collapse=False,
140
140
  style="class:pager.border",
141
141
  ),
142
- Box(
143
- DynamicContainer(lambda: self.output),
144
- padding=0,
145
- padding_left=1,
146
- ),
142
+ Box(self.output, padding=0, padding_left=1),
147
143
  ],
148
144
  style="class:pager",
149
145
  key_bindings=load_registered_bindings(
150
- "euporie.core.widgets.pager.Pager"
146
+ "euporie.core.widgets.pager.Pager",
147
+ config=get_app().config,
151
148
  ),
152
149
  height=height,
153
150
  ),
@@ -14,15 +14,15 @@ from prompt_toolkit.layout.containers import ScrollOffsets
14
14
  from prompt_toolkit.layout.controls import UIContent, UIControl
15
15
  from prompt_toolkit.mouse_events import MouseEvent, MouseEventType
16
16
 
17
+ from euporie.core.app.current import get_app
18
+ from euporie.core.bars.status import StatusContainer
17
19
  from euporie.core.commands import Command, add_cmd, commands
18
- from euporie.core.current import get_app
19
20
  from euporie.core.key_binding.registry import register_bindings
20
21
  from euporie.core.layout.containers import HSplit, VSplit, Window
21
22
  from euporie.core.layout.decor import FocusedStyle
22
23
  from euporie.core.margins import MarginContainer, ScrollbarMargin
23
24
  from euporie.core.widgets.dialog import Dialog
24
25
  from euporie.core.widgets.forms import Text
25
- from euporie.core.widgets.status import StatusContainer
26
26
 
27
27
  if TYPE_CHECKING:
28
28
  from prompt_toolkit.buffer import Buffer
@@ -31,8 +31,8 @@ if TYPE_CHECKING:
31
31
  from prompt_toolkit.key_binding.key_processor import KeyPressEvent
32
32
  from prompt_toolkit.layout.controls import GetLinePrefixCallable
33
33
 
34
- from euporie.core.app import BaseApp
35
- from euporie.core.widgets.status import StatusBarFields
34
+ from euporie.core.app.app import BaseApp
35
+ from euporie.core.bars.status import StatusBarFields
36
36
 
37
37
  log = logging.getLogger(__name__)
38
38
 
@@ -242,7 +242,7 @@ class CommandPalette(Dialog):
242
242
  regex = re.compile(
243
243
  "(?=({}))".format(".*?".join(map(re.escape, buffer.text))), re.IGNORECASE
244
244
  )
245
- for cmd in commands.values():
245
+ for cmd in set(commands.values()):
246
246
  if not cmd.hidden():
247
247
  matches = list(regex.finditer(cmd.title))
248
248
  if matches:
@@ -298,7 +298,7 @@ class CommandPalette(Dialog):
298
298
 
299
299
  register_bindings(
300
300
  {
301
- "euporie.core.app.BaseApp": {
301
+ "euporie.core.app.app:BaseApp": {
302
302
  "show-command-palette": "c-@",
303
303
  }
304
304
  }