euporie 2.6.1__tar.gz → 2.7.0__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.
Files changed (140) hide show
  1. {euporie-2.6.1 → euporie-2.7.0}/PKG-INFO +9 -11
  2. {euporie-2.6.1 → euporie-2.7.0}/README.rst +2 -2
  3. {euporie-2.6.1 → euporie-2.7.0}/euporie/console/tabs/console.py +51 -43
  4. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/__init__.py +5 -2
  5. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/app.py +74 -57
  6. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/comm/ipywidgets.py +7 -3
  7. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/config.py +51 -27
  8. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/convert/__init__.py +2 -0
  9. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/convert/datum.py +82 -45
  10. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/convert/formats/ansi.py +1 -2
  11. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/convert/formats/common.py +7 -11
  12. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/convert/formats/ft.py +10 -7
  13. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/convert/formats/png.py +7 -6
  14. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/convert/formats/sixel.py +1 -1
  15. euporie-2.7.0/euporie/core/convert/formats/svg.py +28 -0
  16. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/convert/mime.py +4 -7
  17. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/data_structures.py +24 -22
  18. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/filters.py +16 -2
  19. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/format.py +30 -4
  20. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/ft/ansi.py +2 -1
  21. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/ft/html.py +155 -42
  22. {euporie-2.6.1/euporie/core/widgets → euporie-2.7.0/euporie/core}/graphics.py +225 -227
  23. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/io.py +8 -0
  24. euporie-2.7.0/euporie/core/key_binding/bindings/__init__.py +11 -0
  25. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/key_binding/bindings/basic.py +9 -14
  26. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/key_binding/bindings/micro.py +0 -12
  27. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/key_binding/bindings/mouse.py +107 -80
  28. euporie-2.7.0/euporie/core/key_binding/bindings/page_navigation.py +129 -0
  29. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/key_binding/key_processor.py +9 -1
  30. euporie-2.7.0/euporie/core/layout/__init__.py +1 -0
  31. euporie-2.7.0/euporie/core/layout/containers.py +1011 -0
  32. {euporie-2.6.1/euporie/core/widgets → euporie-2.7.0/euporie/core/layout}/decor.py +4 -221
  33. euporie-2.7.0/euporie/core/layout/print.py +130 -0
  34. euporie-2.7.0/euporie/core/layout/screen.py +75 -0
  35. euporie-2.6.1/euporie/core/widgets/page.py → euporie-2.7.0/euporie/core/layout/scroll.py +166 -111
  36. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/log.py +1 -1
  37. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/margins.py +11 -5
  38. euporie-2.7.0/euporie/core/path.py +138 -0
  39. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/renderer.py +31 -8
  40. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/style.py +2 -0
  41. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/tabs/base.py +2 -1
  42. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/terminal.py +19 -21
  43. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/widgets/cell.py +2 -4
  44. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/widgets/cell_outputs.py +2 -2
  45. euporie-2.7.0/euporie/core/widgets/decor.py +242 -0
  46. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/widgets/dialog.py +5 -5
  47. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/widgets/display.py +32 -12
  48. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/widgets/file_browser.py +3 -4
  49. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/widgets/forms.py +36 -14
  50. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/widgets/inputs.py +171 -99
  51. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/widgets/layout.py +80 -5
  52. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/widgets/menu.py +1 -3
  53. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/widgets/pager.py +3 -3
  54. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/widgets/palette.py +3 -2
  55. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/widgets/status_bar.py +2 -6
  56. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/widgets/tree.py +3 -6
  57. {euporie-2.6.1 → euporie-2.7.0}/euporie/notebook/app.py +8 -8
  58. {euporie-2.6.1 → euporie-2.7.0}/euporie/notebook/tabs/notebook.py +2 -2
  59. {euporie-2.6.1 → euporie-2.7.0}/euporie/notebook/widgets/side_bar.py +1 -1
  60. {euporie-2.6.1 → euporie-2.7.0}/euporie/preview/tabs/notebook.py +2 -2
  61. {euporie-2.6.1 → euporie-2.7.0}/euporie/web/tabs/web.py +6 -1
  62. {euporie-2.6.1 → euporie-2.7.0}/euporie/web/widgets/webview.py +52 -32
  63. {euporie-2.6.1 → euporie-2.7.0}/pyproject.toml +9 -9
  64. euporie-2.6.1/euporie/core/key_binding/bindings/__init__.py +0 -5
  65. euporie-2.6.1/euporie/core/path.py +0 -271
  66. {euporie-2.6.1 → euporie-2.7.0}/.gitignore +0 -0
  67. {euporie-2.6.1 → euporie-2.7.0}/LICENSE +0 -0
  68. {euporie-2.6.1 → euporie-2.7.0}/euporie/console/__init__.py +0 -0
  69. {euporie-2.6.1 → euporie-2.7.0}/euporie/console/__main__.py +0 -0
  70. {euporie-2.6.1 → euporie-2.7.0}/euporie/console/app.py +0 -0
  71. {euporie-2.6.1 → euporie-2.7.0}/euporie/console/py.typed +0 -0
  72. {euporie-2.6.1 → euporie-2.7.0}/euporie/console/tabs/__init__.py +0 -0
  73. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/__main__.py +0 -0
  74. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/border.py +0 -0
  75. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/clipboard.py +0 -0
  76. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/comm/__init__.py +0 -0
  77. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/comm/base.py +0 -0
  78. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/comm/registry.py +0 -0
  79. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/commands.py +0 -0
  80. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/completion.py +0 -0
  81. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/convert/formats/__init__.py +0 -0
  82. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/convert/formats/base64.py +0 -0
  83. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/convert/formats/html.py +0 -0
  84. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/convert/formats/jpeg.py +0 -0
  85. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/convert/formats/markdown.py +0 -0
  86. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/convert/formats/pdf.py +0 -0
  87. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/convert/formats/pil.py +0 -0
  88. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/convert/formats/rich.py +0 -0
  89. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/convert/registry.py +0 -0
  90. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/convert/utils.py +0 -0
  91. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/current.py +0 -0
  92. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/ft/__init__.py +0 -0
  93. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/ft/table.py +0 -0
  94. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/ft/utils.py +0 -0
  95. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/history.py +0 -0
  96. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/kernel.py +0 -0
  97. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/key_binding/__init__.py +0 -0
  98. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/key_binding/bindings/completion.py +0 -0
  99. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/key_binding/micro_state.py +0 -0
  100. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/key_binding/registry.py +0 -0
  101. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/key_binding/utils.py +0 -0
  102. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/key_binding/vi_state.py +0 -0
  103. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/keys.py +0 -0
  104. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/launch.py +0 -0
  105. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/lexers.py +0 -0
  106. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/processors.py +0 -0
  107. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/py.typed +0 -0
  108. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/pygments.py +0 -0
  109. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/reference.py +0 -0
  110. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/suggest.py +0 -0
  111. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/tabs/__init__.py +0 -0
  112. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/tabs/notebook.py +0 -0
  113. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/utils.py +0 -0
  114. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/validation.py +0 -0
  115. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/widgets/__init__.py +0 -0
  116. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/widgets/formatted_text_area.py +0 -0
  117. {euporie-2.6.1 → euporie-2.7.0}/euporie/core/widgets/search_bar.py +0 -0
  118. {euporie-2.6.1 → euporie-2.7.0}/euporie/data/desktop/euporie-console.desktop +0 -0
  119. {euporie-2.6.1 → euporie-2.7.0}/euporie/data/desktop/euporie-notebook.desktop +0 -0
  120. {euporie-2.6.1 → euporie-2.7.0}/euporie/hub/__init__.py +0 -0
  121. {euporie-2.6.1 → euporie-2.7.0}/euporie/hub/__main__.py +0 -0
  122. {euporie-2.6.1 → euporie-2.7.0}/euporie/hub/app.py +0 -0
  123. {euporie-2.6.1 → euporie-2.7.0}/euporie/hub/py.typed +0 -0
  124. {euporie-2.6.1 → euporie-2.7.0}/euporie/notebook/__init__.py +0 -0
  125. {euporie-2.6.1 → euporie-2.7.0}/euporie/notebook/__main__.py +0 -0
  126. {euporie-2.6.1 → euporie-2.7.0}/euporie/notebook/current.py +0 -0
  127. {euporie-2.6.1 → euporie-2.7.0}/euporie/notebook/enums.py +0 -0
  128. {euporie-2.6.1 → euporie-2.7.0}/euporie/notebook/filters.py +0 -0
  129. {euporie-2.6.1 → euporie-2.7.0}/euporie/notebook/py.typed +0 -0
  130. {euporie-2.6.1 → euporie-2.7.0}/euporie/notebook/tabs/__init__.py +0 -0
  131. {euporie-2.6.1 → euporie-2.7.0}/euporie/notebook/tabs/display.py +0 -0
  132. {euporie-2.6.1 → euporie-2.7.0}/euporie/notebook/tabs/edit.py +0 -0
  133. {euporie-2.6.1 → euporie-2.7.0}/euporie/notebook/tabs/json.py +0 -0
  134. {euporie-2.6.1 → euporie-2.7.0}/euporie/notebook/tabs/log.py +0 -0
  135. {euporie-2.6.1 → euporie-2.7.0}/euporie/notebook/widgets/__init__.py +0 -0
  136. {euporie-2.6.1 → euporie-2.7.0}/euporie/preview/__init__.py +0 -0
  137. {euporie-2.6.1 → euporie-2.7.0}/euporie/preview/__main__.py +0 -0
  138. {euporie-2.6.1 → euporie-2.7.0}/euporie/preview/app.py +0 -0
  139. {euporie-2.6.1 → euporie-2.7.0}/euporie/preview/py.typed +0 -0
  140. {euporie-2.6.1 → euporie-2.7.0}/euporie/preview/tabs/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: euporie
3
- Version: 2.6.1
3
+ Version: 2.7.0
4
4
  Summary: Euporie is a suite of terminal applications for interacting with Jupyter kernels
5
5
  Project-URL: Documentation, https://euporie.readthedocs.io/en/latest
6
6
  Project-URL: Issues, https://github.com/joouha/euporie/issues
@@ -24,9 +24,10 @@ Requires-Python: >=3.8
24
24
  Requires-Dist: aenum<=3.1.12,~=3.1
25
25
  Requires-Dist: fastjsonschema~=2.15
26
26
  Requires-Dist: flatlatex~=0.15
27
- Requires-Dist: fsspec[http]>=2022.8.0
27
+ Requires-Dist: fsspec[http]>=2022.12.0
28
28
  Requires-Dist: imagesize~=1.3
29
29
  Requires-Dist: jupyter-client>=7.1
30
+ Requires-Dist: jupytext>=1.14.0
30
31
  Requires-Dist: linkify-it-py~=1.0
31
32
  Requires-Dist: markdown-it-py~=2.1.0
32
33
  Requires-Dist: mdit-py-plugins~=0.3.0
@@ -36,19 +37,16 @@ Requires-Dist: platformdirs~=3.5
36
37
  Requires-Dist: prompt-toolkit~=3.0.36
37
38
  Requires-Dist: pygments~=2.11
38
39
  Requires-Dist: pyperclip~=1.8
39
- Requires-Dist: sixelcrop~=0.1.3
40
- Requires-Dist: timg~=1.1
40
+ Requires-Dist: sixelcrop~=0.1.6
41
+ Requires-Dist: timg~=1.1.6
41
42
  Requires-Dist: typing-extensions~=4.5
42
- Requires-Dist: universal-pathlib<0.0.24
43
- Provides-Extra: chafa
44
- Requires-Dist: chafa-py>=1.0.2; extra == 'chafa'
43
+ Requires-Dist: universal-pathlib~=0.1.4
45
44
  Provides-Extra: format
46
45
  Requires-Dist: black>=19.3.b0; extra == 'format'
47
46
  Requires-Dist: isort~=5.10.1; extra == 'format'
47
+ Requires-Dist: ruff~=0.1.0; extra == 'format'
48
48
  Provides-Extra: hub
49
49
  Requires-Dist: asyncssh~=2.10.1; extra == 'hub'
50
- Provides-Extra: jupytext
51
- Requires-Dist: jupytext>=1.14.0; extra == 'jupytext'
52
50
  Description-Content-Type: text/x-rst
53
51
 
54
52
  |logo|
@@ -80,8 +78,8 @@ If you're working with Jupyter notebooks in a terminal only environment, like an
80
78
  - `Preview <https://euporie.readthedocs.io/en/latest/apps/preview.html>`_
81
79
  - `Hub <https://euporie.readthedocs.io/en/latest/apps/hub.html>`_
82
80
 
83
- .. image:: https://user-images.githubusercontent.com/12154190/226652401-f0601134-208d-4edc-9f5d-39fcd9dab288.png
84
- :target: https://user-images.githubusercontent.com/12154190/226652401-f0601134-208d-4edc-9f5d-39fcd9dab288.png
81
+ .. image:: https://github.com/joouha/euporie/assets/12154190/c8ea6e23-11bb-4ffc-a9e5-111f788c51ae
82
+ :target: https://github.com/joouha/euporie/assets/12154190/c8ea6e23-11bb-4ffc-a9e5-111f788c51ae
85
83
 
86
84
  `View more screenshots here <https://euporie.readthedocs.io/en/latest/pages/gallery.html>`_
87
85
 
@@ -27,8 +27,8 @@ If you're working with Jupyter notebooks in a terminal only environment, like an
27
27
  - `Preview <https://euporie.readthedocs.io/en/latest/apps/preview.html>`_
28
28
  - `Hub <https://euporie.readthedocs.io/en/latest/apps/hub.html>`_
29
29
 
30
- .. image:: https://user-images.githubusercontent.com/12154190/226652401-f0601134-208d-4edc-9f5d-39fcd9dab288.png
31
- :target: https://user-images.githubusercontent.com/12154190/226652401-f0601134-208d-4edc-9f5d-39fcd9dab288.png
30
+ .. image:: https://github.com/joouha/euporie/assets/12154190/c8ea6e23-11bb-4ffc-a9e5-111f788c51ae
31
+ :target: https://github.com/joouha/euporie/assets/12154190/c8ea6e23-11bb-4ffc-a9e5-111f788c51ae
32
32
 
33
33
  `View more screenshots here <https://euporie.readthedocs.io/en/latest/pages/gallery.html>`_
34
34
 
@@ -7,7 +7,6 @@ from functools import partial
7
7
  from typing import TYPE_CHECKING, cast
8
8
 
9
9
  import nbformat
10
- from prompt_toolkit.application.run_in_terminal import in_terminal
11
10
  from prompt_toolkit.buffer import Buffer, ValidationState
12
11
  from prompt_toolkit.filters.app import (
13
12
  buffer_has_focus,
@@ -44,13 +43,13 @@ from euporie.core.key_binding.registry import (
44
43
  load_registered_bindings,
45
44
  register_bindings,
46
45
  )
46
+ from euporie.core.layout.print import PrintingContainer
47
47
  from euporie.core.style import KERNEL_STATUS_REPR
48
48
  from euporie.core.tabs.base import KernelTab
49
49
  from euporie.core.terminal import edit_in_editor
50
50
  from euporie.core.validation import KernelValidator
51
51
  from euporie.core.widgets.cell_outputs import CellOutputArea
52
52
  from euporie.core.widgets.inputs import KernelInput, StdInput
53
- from euporie.core.widgets.page import PrintingContainer
54
53
  from euporie.core.widgets.pager import PagerState
55
54
 
56
55
  if TYPE_CHECKING:
@@ -177,8 +176,9 @@ class Console(KernelTab):
177
176
  """Run the code in the input box."""
178
177
  if buffer is None:
179
178
  buffer = self.input_box.buffer
179
+ app = self.app
180
180
  # Auto-reformat code
181
- if self.app.config.autoformat:
181
+ if app.config.autoformat:
182
182
  self.reformat()
183
183
  # Get the code to run
184
184
  text = buffer.text
@@ -187,9 +187,12 @@ class Console(KernelTab):
187
187
  # Disable existing output
188
188
  self.live_output.style = "class:disabled"
189
189
  # Re-render the app and move to below the current output
190
- self.app.draw()
190
+ original_layout = app.layout
191
+ app.layout = self.input_layout
192
+ app.draw()
193
+ app.layout = original_layout
191
194
  # Prevent displayed graphics on terminal being cleaned up (bit of a hack)
192
- self.app.graphics.clear()
195
+ app.graphics.clear()
193
196
  # Run the previous entry
194
197
  if self.kernel.status == "starting":
195
198
  self.kernel_queue.append(partial(self.kernel.run, text, wait=False))
@@ -201,15 +204,15 @@ class Console(KernelTab):
201
204
  buffer.reset(append_to_history=True)
202
205
  # Remove any live outputs and disable mouse support
203
206
  self.live_output.reset()
204
- if self.app.config.mouse_support is None:
205
- self.app.need_mouse_support = False
207
+ if app.config.mouse_support is None:
208
+ app.need_mouse_support = False
206
209
  # Record the input as a cell in the json
207
210
  self.json["cells"].append(
208
211
  nbformat.v4.new_code_cell(source=text, execution_count=self.execution_count)
209
212
  )
210
213
  if (
211
- self.app.config.max_stored_outputs
212
- and len(self.json["cells"]) > self.app.config.max_stored_outputs
214
+ app.config.max_stored_outputs
215
+ and len(self.json["cells"]) > app.config.max_stored_outputs
213
216
  ):
214
217
  del self.json["cells"][0]
215
218
 
@@ -241,14 +244,12 @@ class Console(KernelTab):
241
244
  def render_outputs(self, app: Application[Any]) -> None:
242
245
  """Request that any unrendered outputs be rendered."""
243
246
  if self.output.json:
244
- self.app.create_background_task(self.async_render_outputs())
245
-
246
- async def async_render_outputs(self) -> None:
247
- """Render any unrendered outputs above the application."""
248
- if self.output.json:
249
- # Run the output app in the terminal
250
- async with in_terminal():
251
- self.app.renderer.render(self.app, self.output_layout, is_done=True)
247
+ app = self.app
248
+ original_layout = self.app.layout
249
+ app.layout = self.output_layout
250
+ app.renderer.render(self.app, self.output_layout, is_done=True)
251
+ app.renderer.request_absolute_cursor_position()
252
+ app.layout = original_layout
252
253
  # Remove the outputs so they do not get rendered again
253
254
  self.output.reset()
254
255
 
@@ -410,9 +411,13 @@ class Console(KernelTab):
410
411
  height=1,
411
412
  )
412
413
 
414
+ def _handler(buffer: Buffer) -> bool:
415
+ self.run(buffer)
416
+ return True
417
+
413
418
  self.input_box = KernelInput(
414
419
  kernel_tab=self,
415
- accept_handler=self.run,
420
+ accept_handler=_handler,
416
421
  on_cursor_position_changed=on_cursor_position_changed,
417
422
  validator=KernelValidator(self.kernel),
418
423
  # validate_while_typing=False,
@@ -424,8 +429,34 @@ class Console(KernelTab):
424
429
  self.app.focused_element = self.input_box.buffer
425
430
 
426
431
  self.stdin_box = StdInput(self)
432
+ input_row = [
433
+ # Spacing
434
+ ConditionalContainer(
435
+ Window(height=1, dont_extend_height=True),
436
+ filter=Condition(lambda: self.execution_count > 0)
437
+ & (
438
+ (
439
+ renderer_height_is_known
440
+ & Condition(lambda: self.app.renderer.rows_above_layout > 0)
441
+ )
442
+ | ~renderer_height_is_known
443
+ ),
444
+ ),
445
+ # Input
446
+ ConditionalContainer(
447
+ VSplit(
448
+ [
449
+ input_prompt,
450
+ self.input_box,
451
+ ],
452
+ ),
453
+ filter=~self.stdin_box.visible,
454
+ ),
455
+ ]
456
+
457
+ self.input_layout = Layout(PrintingContainer(input_row))
427
458
 
428
- self.input_layout = HSplit(
459
+ return HSplit(
429
460
  [
430
461
  ConditionalContainer(
431
462
  HSplit(
@@ -442,36 +473,13 @@ class Console(KernelTab):
442
473
  Window(height=1, dont_extend_height=True),
443
474
  filter=self.stdin_box.visible,
444
475
  ),
445
- # Spacing
446
- ConditionalContainer(
447
- Window(height=1, dont_extend_height=True),
448
- filter=Condition(lambda: self.execution_count > 0)
449
- & (
450
- (
451
- renderer_height_is_known
452
- & Condition(lambda: self.app.renderer.rows_above_layout > 0)
453
- )
454
- | ~renderer_height_is_known
455
- ),
456
- ),
457
- # Input
458
- ConditionalContainer(
459
- VSplit(
460
- [
461
- input_prompt,
462
- self.input_box,
463
- ],
464
- ),
465
- filter=~self.stdin_box.visible,
466
- ),
476
+ *input_row,
467
477
  ],
468
478
  key_bindings=load_registered_bindings(
469
479
  "euporie.console.tabs.console.Console"
470
480
  ),
471
481
  )
472
482
 
473
- return self.input_layout
474
-
475
483
  def set_next_input(self, text: str, replace: bool = False) -> None:
476
484
  """Set the text for the next prompt."""
477
485
  self.input_box.buffer.text = text
@@ -1,15 +1,18 @@
1
1
  """This package defines the euporie application and its components."""
2
2
 
3
3
  __app_name__ = "euporie"
4
- __version__ = "2.6.1"
4
+ __version__ = "2.7.0"
5
5
  __logo__ = "⚈"
6
6
  __strapline__ = "Jupyter in the terminal"
7
7
  __author__ = "Josiah Outram Halstead"
8
8
  __email__ = "josiah@halstead.email"
9
- __copyright__ = f"© 2022, {__author__}"
9
+ __copyright__ = f"© 2023, {__author__}"
10
10
  __license__ = "MIT"
11
11
 
12
12
 
13
13
  # Register extensions to external packages
14
14
  from euporie.core import path # noqa F401
15
15
  from euporie.core import pygments # noqa F401
16
+
17
+ # Monkey-patch prompt_toolkit
18
+ from euporie.core.layout import containers # noqa: F401
@@ -39,7 +39,7 @@ from prompt_toolkit.key_binding.key_bindings import (
39
39
  ConditionalKeyBindings,
40
40
  merge_key_bindings,
41
41
  )
42
- from prompt_toolkit.layout.containers import Float, FloatContainer, Window, to_container
42
+ from prompt_toolkit.layout.containers import Float, FloatContainer, to_container
43
43
  from prompt_toolkit.layout.layout import Layout
44
44
  from prompt_toolkit.output import ColorDepth
45
45
  from prompt_toolkit.output.defaults import create_output
@@ -64,7 +64,7 @@ from euporie.core.commands import add_cmd
64
64
  from euporie.core.config import Config, add_setting
65
65
  from euporie.core.convert.mime import get_mime
66
66
  from euporie.core.current import get_app
67
- from euporie.core.filters import in_tmux, insert_mode, replace_mode, tab_has_focus
67
+ from euporie.core.filters import in_mplex, insert_mode, replace_mode, tab_has_focus
68
68
  from euporie.core.io import Vt100_Output, Vt100Parser
69
69
  from euporie.core.key_binding.key_processor import KeyProcessor
70
70
  from euporie.core.key_binding.micro_state import MicroState
@@ -73,6 +73,7 @@ from euporie.core.key_binding.registry import (
73
73
  register_bindings,
74
74
  )
75
75
  from euporie.core.key_binding.vi_state import ViState
76
+ from euporie.core.layout.containers import Window
76
77
  from euporie.core.log import setup_logs
77
78
  from euporie.core.path import parse_path
78
79
  from euporie.core.renderer import Renderer
@@ -194,6 +195,7 @@ class BaseApp(Application):
194
195
  leave_graphics: FilterOrBool = True,
195
196
  extend_renderer_height: FilterOrBool = False,
196
197
  extend_renderer_width: FilterOrBool = False,
198
+ enable_page_navigation_bindings: FilterOrBool | None = True,
197
199
  **kwargs: Any,
198
200
  ) -> None:
199
201
  """Instantiate euporie specific application variables.
@@ -210,6 +212,8 @@ class BaseApp(Application):
210
212
  beyond the height of the display
211
213
  extend_renderer_width: Whether the renderer width should be extended
212
214
  beyond the height of the display
215
+ enable_page_navigation_bindings: Determines if page navigation keybindings
216
+ should be loaded
213
217
  kwargs: The key-word arguments for the :py:class:`Application`
214
218
 
215
219
  """
@@ -220,6 +224,7 @@ class BaseApp(Application):
220
224
  "editing_mode": self.get_edit_mode(),
221
225
  "mouse_support": Condition(lambda: self.need_mouse_support),
222
226
  "cursor": CursorConfig(),
227
+ "enable_page_navigation_bindings": enable_page_navigation_bindings,
223
228
  **kwargs,
224
229
  }
225
230
  )
@@ -270,6 +275,12 @@ class BaseApp(Application):
270
275
  "euporie.core.app.BaseApp",
271
276
  "euporie.core.terminal.TerminalInfo",
272
277
  ]
278
+
279
+ from euporie.core.key_binding.bindings.page_navigation import (
280
+ load_page_navigation_bindings,
281
+ )
282
+
283
+ self._page_navigation_bindings = load_page_navigation_bindings(self.config)
273
284
  # Determines which clipboard mechanism to use
274
285
  self.clipboard: Clipboard = EuporieClipboard(self)
275
286
  # Allow hiding element when manually redrawing app
@@ -365,10 +376,8 @@ class BaseApp(Application):
365
376
  with set_app(self):
366
377
  # Load key bindings
367
378
  self.load_key_bindings()
368
-
369
379
  # Send queries to the terminal
370
380
  self.term_info.send_all()
371
-
372
381
  # Read responses
373
382
  kp = self.key_processor
374
383
 
@@ -376,9 +385,9 @@ class BaseApp(Application):
376
385
  kp.feed_multiple(self.input.read_keys())
377
386
 
378
387
  with self.input.raw_mode(), self.input.attach(read_from_input):
379
- # Wait up to half a second for the terminal to respond to queries
380
- await asyncio.sleep(0.5)
381
-
388
+ # Give the terminal time to respond and allow the event loop to read
389
+ # the terminal responses from the input
390
+ await asyncio.sleep(0.1)
382
391
  kp.process_keys()
383
392
 
384
393
  return await super().run_async(
@@ -495,12 +504,10 @@ class BaseApp(Application):
495
504
  setup_logs()
496
505
  # Load the app's configuration
497
506
  cls.config.load(cls)
498
- # Configure the logs
499
- setup_logs(cls.config)
500
- # Warn about unrecognised configuration items
501
- cls.config.warn()
502
507
  # Run the application
503
- with create_app_session(input=cls.load_input(), output=cls.load_output()):
508
+ with create_app_session(
509
+ input=cls.load_input(), output=(output := cls.load_output())
510
+ ):
504
511
  # Create an instance of the app and run it
505
512
 
506
513
  original_sigterm = signal.getsignal(signal.SIGTERM)
@@ -516,7 +523,10 @@ class BaseApp(Application):
516
523
  signal.signal(signal.SIGTERM, original_sigterm)
517
524
  signal.signal(signal.SIGINT, original_sigint)
518
525
 
519
- return result
526
+ # This seems to be needed for kitty
527
+ output.enable_autowrap()
528
+
529
+ return result
520
530
 
521
531
  def cleanup(self, signum: int, frame: FrameType | None) -> None:
522
532
  """Restore the state of the terminal on unexpected exit."""
@@ -524,13 +534,9 @@ class BaseApp(Application):
524
534
  output = self.output
525
535
  self.exit()
526
536
  # Reset terminal state
527
- # output.quit_alternate_screen()
528
- # output.disable_mouse_support()
529
537
  output.reset_cursor_key_mode()
530
538
  output.enable_autowrap()
531
- # output.disable_bracketed_paste()
532
539
  output.clear_title()
533
- # output.reset_cursor_shape()
534
540
  output.show_cursor()
535
541
  output.reset_attributes()
536
542
  self.renderer.reset()
@@ -738,6 +744,16 @@ class BaseApp(Application):
738
744
  "default" if name in ("fg", "bg") else name,
739
745
  )
740
746
  # Add accent color
747
+ # self.color_palette.add_color(
748
+ # "hl",
749
+ # (bg := self.color_palette.bg)
750
+ # .adjust(
751
+ # hue=(bg.hue + (bg.hue - 0.036)) % 1,
752
+ # saturation=(0.88 - bg.saturation),
753
+ # brightness=0.4255 - bg.brightness,
754
+ # )
755
+ # .base_hex,
756
+ # )
741
757
  self.color_palette.add_color(
742
758
  "hl", base_colors.get(self.config.accent_color, self.config.accent_color)
743
759
  )
@@ -778,6 +794,8 @@ class BaseApp(Application):
778
794
  ) -> None:
779
795
  """Update the application's style when the syntax theme is changed."""
780
796
  self.renderer.style = self.create_merged_style()
797
+ # Trigger a re-draw
798
+ self.invalidate()
781
799
 
782
800
  def refresh(self) -> None:
783
801
  """Reet all tabs."""
@@ -933,11 +951,11 @@ class BaseApp(Application):
933
951
  add_setting(
934
952
  name="terminal_polling_interval",
935
953
  flags=["--terminal-polling-interval"],
936
- type_=int,
954
+ type_=float,
937
955
  help_="Time between terminal colour queries",
938
- default=0,
956
+ default=0.0,
939
957
  schema={
940
- "min": 0,
958
+ "min": 0.0,
941
959
  },
942
960
  description="""
943
961
  Determine how frequently the terminal should be polled for changes to the
@@ -957,35 +975,27 @@ class BaseApp(Application):
957
975
  )
958
976
 
959
977
  add_setting(
960
- name="format_black",
961
- flags=["--format-black"],
962
- type_=bool,
963
- help_="Use black when re-formatting code cells",
964
- default=True,
965
- description="""
966
- Whether to use :py:mod:`black` when reformatting code cells.
967
- """,
968
- )
969
-
970
- add_setting(
971
- name="format_isort",
972
- flags=["--format-isort"],
973
- type_=bool,
974
- help_="Use isort when re-formatting code cells",
975
- default=True,
976
- description="""
977
- Whether to use :py:mod:`isort` when reformatting code cells.
978
- """,
979
- )
980
-
981
- add_setting(
982
- name="format_ssort",
983
- flags=["--format-ssort"],
984
- type_=bool,
985
- help_="Use ssort when re-formatting code cells",
986
- default=True,
978
+ name="formatters",
979
+ flags=["--formatters"],
980
+ type_=str,
981
+ choices=["ruff", "black", "isort", "ssort"],
982
+ help_="List formatters to use when re-formatting code cells",
983
+ default=["ruff"],
984
+ action="append",
985
+ schema={
986
+ "type": "array",
987
+ "items": {
988
+ "description": "Formatters",
989
+ "type": "string",
990
+ },
991
+ },
987
992
  description="""
988
- Whether to use :py:mod:`ssort` when reformatting code cells.
993
+ A list of the names of the formatters to use when reformatting code cells.
994
+ Supported formatters include:
995
+ - :py:mod:`ruff`
996
+ - :py:mod:`black`
997
+ - :py:mod:`isort`
998
+ - :py:mod:`ssort`
989
999
  """,
990
1000
  )
991
1001
 
@@ -1020,24 +1030,31 @@ class BaseApp(Application):
1020
1030
  )
1021
1031
 
1022
1032
  add_setting(
1023
- name="tmux_graphics",
1024
- flags=["--tmux-graphics"],
1033
+ name="multiplexer_passthrough",
1034
+ flags=["--multiplexer-passthrough"],
1025
1035
  type_=bool,
1026
- help_="Enable terminal graphics in tmux (experimental)",
1036
+ help_="Use passthrough from within terminal multiplexers",
1027
1037
  default=False,
1028
- hidden=~in_tmux,
1038
+ hidden=~in_mplex,
1029
1039
  description="""
1030
- If set, terminal graphics will be used if :program:`tmux` is running by
1031
- performing terminal escape sequence pass-through. You must restart euporie
1032
- for this to take effect.
1040
+ If set and euporie is running inside a terminal multiplexer
1041
+ (:program:`screen` or :program:`tmux`), then certain escape sequences
1042
+ will be passed-through the multiplexer directly to the terminal.
1033
1043
 
1034
- You will also need to ensure that ``allow-passthrough`` is set to ``on`` in
1035
- your :program:`tmux` configuration.
1044
+ This affects things such as terminal color detection and graphics display.
1045
+
1046
+ for tmux, you will also need to ensure that ``allow-passthrough`` is set to
1047
+ ``on`` in your :program:`tmux` configuration.
1036
1048
 
1037
1049
  .. warning::
1038
1050
 
1039
1051
  Terminal graphics in :program:`tmux` is experimental, and is not
1040
1052
  guaranteed to work. Use at your own risk!
1053
+
1054
+ .. note::
1055
+ As of version :command:`tmux` version ``3.4`` sixel graphics are
1056
+ supported, which may result in better terminal graphics then using
1057
+ multiplexer passthrough.
1041
1058
  """,
1042
1059
  )
1043
1060
 
@@ -16,14 +16,13 @@ from typing import TYPE_CHECKING
16
16
  from prompt_toolkit.filters.base import Condition
17
17
  from prompt_toolkit.layout.containers import HSplit, VSplit
18
18
  from prompt_toolkit.layout.processors import BeforeInput
19
- from prompt_toolkit.widgets.base import Box
20
19
 
21
20
  from euporie.core.comm.base import Comm, CommView
22
21
  from euporie.core.convert.datum import Datum
23
22
  from euporie.core.data_structures import DiBool
24
23
  from euporie.core.kernel import MsgCallbacks
24
+ from euporie.core.layout.decor import FocusedStyle
25
25
  from euporie.core.widgets.cell_outputs import CellOutputArea
26
- from euporie.core.widgets.decor import FocusedStyle
27
26
  from euporie.core.widgets.display import Display
28
27
  from euporie.core.widgets.forms import (
29
28
  Button,
@@ -39,7 +38,12 @@ from euporie.core.widgets.forms import (
39
38
  ToggleButton,
40
39
  ToggleButtons,
41
40
  )
42
- from euporie.core.widgets.layout import AccordionSplit, ReferencedSplit, TabbedSplit
41
+ from euporie.core.widgets.layout import (
42
+ AccordionSplit,
43
+ Box,
44
+ ReferencedSplit,
45
+ TabbedSplit,
46
+ )
43
47
 
44
48
  if TYPE_CHECKING:
45
49
  from typing import Any, Iterable, MutableSequence, Sequence