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
@@ -3,9 +3,11 @@
3
3
 
4
4
  def main() -> "None":
5
5
  """Call the main entrypoint to the application."""
6
+ import sys
7
+
6
8
  from euporie.core import __main__
7
9
 
8
- __main__.main(__name__.split(".")[1])
10
+ __main__.main(str(sys.modules[__name__].__package__).rpartition(".")[2])
9
11
 
10
12
 
11
13
  if __name__ == "__main__":
euporie/console/app.py CHANGED
@@ -22,7 +22,7 @@ from prompt_toolkit.layout.containers import (
22
22
  )
23
23
  from prompt_toolkit.layout.dimension import Dimension
24
24
 
25
- from euporie.console.tabs.console import ConsoleTab
25
+ from euporie.console.tabs.console import Console
26
26
  from euporie.core import __logo__
27
27
  from euporie.core.app import BaseApp
28
28
  from euporie.core.commands import add_cmd
@@ -82,7 +82,7 @@ class ConsoleApp(BaseApp):
82
82
  self.search_bar = SearchBar()
83
83
  self.bindings_to_load += ["euporie.console.app.ConsoleApp"]
84
84
 
85
- self.tabs = [ConsoleTab(self)]
85
+ self.tabs = []
86
86
  self.pager = Pager()
87
87
 
88
88
  self.config.get_item("mouse_support").event += lambda x: setattr(
@@ -99,6 +99,8 @@ class ConsoleApp(BaseApp):
99
99
 
100
100
  def load_container(self) -> FloatContainer:
101
101
  """Return a container with all opened tabs."""
102
+ self.tabs = [Console(self)]
103
+
102
104
  assert self.pager is not None
103
105
  assert self.search_bar is not None
104
106
  assert self.tab is not None
@@ -166,7 +168,7 @@ class ConsoleApp(BaseApp):
166
168
  app = get_app()
167
169
  nb_app = NotebookApp()
168
170
  for tab in app.tabs:
169
- if isinstance(tab, ConsoleTab):
171
+ if isinstance(tab, Console):
170
172
  nb = Notebook(
171
173
  app=nb_app,
172
174
  path=tab.path,
@@ -195,7 +197,7 @@ class ConsoleApp(BaseApp):
195
197
  app = get_app()
196
198
  tab = app.tab
197
199
  app.renderer.clear()
198
- if isinstance(tab, ConsoleTab):
200
+ if isinstance(tab, Console):
199
201
  tab.reset()
200
202
  app.layout.focus(tab.input_box)
201
203
 
@@ -28,6 +28,7 @@ from prompt_toolkit.layout.containers import (
28
28
  )
29
29
  from prompt_toolkit.layout.controls import FormattedTextControl
30
30
  from prompt_toolkit.layout.layout import Layout
31
+ from upath import UPath
31
32
 
32
33
  from euporie.core.commands import add_cmd, get_cmd
33
34
  from euporie.core.config import add_setting
@@ -52,6 +53,7 @@ from euporie.core.widgets.page import PrintingContainer
52
53
  from euporie.core.widgets.pager import PagerState
53
54
 
54
55
  if TYPE_CHECKING:
56
+ from pathlib import Path
55
57
  from typing import Any, Callable, Sequence
56
58
 
57
59
  from prompt_toolkit.application.application import Application
@@ -59,32 +61,35 @@ if TYPE_CHECKING:
59
61
  from prompt_toolkit.key_binding.key_bindings import NotImplementedOrNone
60
62
  from prompt_toolkit.key_binding.key_processor import KeyPressEvent
61
63
  from prompt_toolkit.layout.containers import Float
62
- from upath import UPath
63
64
 
64
65
  from euporie.core.app import BaseApp
65
66
 
66
67
  log = logging.getLogger(__name__)
67
68
 
68
69
 
69
- class ConsoleTab(KernelTab):
70
+ class Console(KernelTab):
70
71
  """Interactive console.
71
72
 
72
73
  An interactive console which connects to a Jupyter kernel.
73
74
 
74
75
  """
75
76
 
77
+ bg_init = False
78
+
76
79
  def __init__(
77
80
  self,
78
81
  app: BaseApp,
79
- path: UPath | None = None,
82
+ path: Path | None = None,
80
83
  use_kernel_history: bool = True,
84
+ connection_file: str = "",
81
85
  ) -> None:
82
- """Create a new :py:class:`ConsoleTab` tab instance.
86
+ """Create a new :py:class:`Console` tab instance.
83
87
 
84
88
  Args:
85
89
  app: The euporie application the console tab belongs to
86
90
  path: A file path to open (not used currently)
87
91
  use_kernel_history: If :const:`True`, history will be loaded from the kernel
92
+ connection_file: The connection file of an existing kernel
88
93
  """
89
94
  # Kernel setup
90
95
  self._metadata = {}
@@ -105,7 +110,12 @@ class ConsoleTab(KernelTab):
105
110
  )
106
111
  self.kernel_tab = self
107
112
 
108
- super().__init__(app=app, path=path, use_kernel_history=use_kernel_history)
113
+ super().__init__(
114
+ app=app,
115
+ path=path,
116
+ use_kernel_history=use_kernel_history,
117
+ connection_file=app.config.connection_file,
118
+ )
109
119
 
110
120
  self.lang_info: dict[str, Any] = {}
111
121
  self.execution_count = 0
@@ -477,7 +487,7 @@ class ConsoleTab(KernelTab):
477
487
  """Request the output is refreshed (does nothing)."""
478
488
  pass
479
489
 
480
- def statusbar_fields(
490
+ def __pt_status__(
481
491
  self,
482
492
  ) -> tuple[Sequence[AnyFormattedText], Sequence[AnyFormattedText]]:
483
493
  """Generate the formatted text for the statusbar."""
@@ -534,7 +544,7 @@ class ConsoleTab(KernelTab):
534
544
  callback=_cb,
535
545
  )
536
546
 
537
- def save(self, path: UPath | None = None, cb: Callable | None = None) -> None:
547
+ def save(self, path: Path | None = None, cb: Callable | None = None) -> None:
538
548
  """Save the console as a notebook."""
539
549
  from euporie.core.tabs.notebook import BaseNotebook
540
550
 
@@ -573,7 +583,7 @@ class ConsoleTab(KernelTab):
573
583
  from euporie.console.app import get_app
574
584
 
575
585
  console = get_app().tab
576
- assert isinstance(console, ConsoleTab)
586
+ assert isinstance(console, Console)
577
587
  console.run()
578
588
 
579
589
  @staticmethod
@@ -585,7 +595,7 @@ class ConsoleTab(KernelTab):
585
595
  from euporie.console.app import get_app
586
596
 
587
597
  console = get_app().tab
588
- assert isinstance(console, ConsoleTab)
598
+ assert isinstance(console, Console)
589
599
  console.inspect()
590
600
 
591
601
  @staticmethod
@@ -644,6 +654,21 @@ class ConsoleTab(KernelTab):
644
654
  """,
645
655
  )
646
656
 
657
+ add_setting(
658
+ name="connection_file",
659
+ flags=["--connection-file", "--kernel-connection-file"],
660
+ type_=UPath,
661
+ help_="Attempt to connect to an existing kernel using a JSON connection info file",
662
+ default=None,
663
+ description="""
664
+ If the file does not exist, kernel connection information will be written
665
+ to the file path provided.
666
+
667
+ If the file exists, kernel connection info will be read from the file,
668
+ allowing euporie to connect to existing kernels.
669
+ """,
670
+ )
671
+
647
672
  # ################################# Key Bindings ##################################
648
673
 
649
674
  register_bindings(
euporie/core/__init__.py CHANGED
@@ -1,10 +1,15 @@
1
1
  """This package defines the euporie application and its components."""
2
2
 
3
3
  __app_name__ = "euporie"
4
- __version__ = "2.3.2"
4
+ __version__ = "2.4.1"
5
5
  __logo__ = "⚈"
6
6
  __strapline__ = "Jupyter in the terminal"
7
7
  __author__ = "Josiah Outram Halstead"
8
8
  __email__ = "josiah@halstead.email"
9
9
  __copyright__ = f"© 2022, {__author__}"
10
10
  __license__ = "MIT"
11
+
12
+
13
+ # Register extensions to external packages
14
+ from euporie.core import path # noqa F401
15
+ from euporie.core import pygments # noqa F401
euporie/core/__main__.py CHANGED
@@ -5,7 +5,7 @@ def main(name: "str" = "launch") -> "None":
5
5
  """Load and launches the application."""
6
6
  from importlib.metadata import entry_points
7
7
 
8
- for entry in entry_points()["euporie.apps"]:
8
+ for entry in entry_points().get("euporie.apps", []):
9
9
  if entry.name == name:
10
10
  return entry.load().launch()
11
11
  else:
euporie/core/app.py CHANGED
@@ -5,7 +5,10 @@ from __future__ import annotations
5
5
  import asyncio
6
6
  import json
7
7
  import logging
8
+ import signal
9
+ import sys
8
10
  from functools import partial
11
+ from pathlib import PurePath
9
12
  from typing import TYPE_CHECKING, cast
10
13
  from weakref import WeakSet
11
14
 
@@ -17,7 +20,6 @@ from prompt_toolkit.cursor_shapes import CursorShape, CursorShapeConfig
17
20
  from prompt_toolkit.data_structures import Point
18
21
  from prompt_toolkit.enums import EditingMode
19
22
  from prompt_toolkit.filters import Condition, buffer_has_focus, to_filter
20
- from prompt_toolkit.formatted_text import to_formatted_text
21
23
  from prompt_toolkit.input.defaults import create_input
22
24
  from prompt_toolkit.key_binding.bindings.basic import load_basic_bindings
23
25
  from prompt_toolkit.key_binding.bindings.cpr import load_cpr_bindings
@@ -39,7 +41,6 @@ from prompt_toolkit.key_binding.key_bindings import (
39
41
  )
40
42
  from prompt_toolkit.layout.containers import Float, FloatContainer, Window, to_container
41
43
  from prompt_toolkit.layout.layout import Layout
42
- from prompt_toolkit.layout.menus import CompletionsMenu
43
44
  from prompt_toolkit.output import ColorDepth
44
45
  from prompt_toolkit.output.defaults import create_output
45
46
  from prompt_toolkit.output.vt100 import Vt100_Output as PtkVt100_Output
@@ -61,6 +62,7 @@ from upath import UPath
61
62
 
62
63
  from euporie.core.commands import add_cmd
63
64
  from euporie.core.config import Config, add_setting
65
+ from euporie.core.convert.core import get_mime
64
66
  from euporie.core.current import get_app
65
67
  from euporie.core.filters import in_tmux, insert_mode, replace_mode, tab_has_focus
66
68
  from euporie.core.io import Vt100_Output, Vt100Parser
@@ -71,7 +73,8 @@ from euporie.core.key_binding.registry import (
71
73
  register_bindings,
72
74
  )
73
75
  from euporie.core.key_binding.vi_state import ViState
74
- from euporie.core.log import default_logs, setup_logs
76
+ from euporie.core.log import setup_logs
77
+ from euporie.core.path import parse_path
75
78
  from euporie.core.renderer import Renderer
76
79
  from euporie.core.style import (
77
80
  DEFAULT_COLORS,
@@ -83,19 +86,20 @@ from euporie.core.style import (
83
86
  build_style,
84
87
  )
85
88
  from euporie.core.terminal import TerminalInfo
86
- from euporie.core.utils import ChainedList, parse_path
89
+ from euporie.core.utils import ChainedList
87
90
  from euporie.core.widgets.decor import Shadow
91
+ from euporie.core.widgets.menu import CompletionsMenu
88
92
 
89
93
  if TYPE_CHECKING:
90
94
  from asyncio import AbstractEventLoop
91
- from typing import Any, Callable, Literal, Sequence
95
+ from pathlib import Path
96
+ from types import FrameType
97
+ from typing import Any, Callable
92
98
 
93
99
  from prompt_toolkit.clipboard import Clipboard
94
100
  from prompt_toolkit.contrib.ssh import PromptToolkitSSHSession
95
101
  from prompt_toolkit.filters import Filter, FilterOrBool
96
- from prompt_toolkit.formatted_text import AnyFormattedText, StyleAndTextTuples
97
102
  from prompt_toolkit.input import Input
98
- from prompt_toolkit.layout.containers import AnyContainer
99
103
  from prompt_toolkit.layout.layout import FocusableElement
100
104
  from prompt_toolkit.layout.screen import WritePosition
101
105
  from prompt_toolkit.output import Output
@@ -107,12 +111,6 @@ if TYPE_CHECKING:
107
111
  from euporie.core.widgets.pager import Pager
108
112
  from euporie.core.widgets.search_bar import SearchBar
109
113
 
110
- StatusBarFields = tuple[Sequence[AnyFormattedText], Sequence[AnyFormattedText]]
111
- ContainerStatusDict = dict[
112
- AnyContainer,
113
- Callable[[], StatusBarFields],
114
- ]
115
-
116
114
  log = logging.getLogger(__name__)
117
115
 
118
116
 
@@ -182,7 +180,6 @@ class BaseApp(Application):
182
180
  mouse_position: Point
183
181
 
184
182
  config = Config()
185
- status_default: StatusBarFields = ([], [])
186
183
  need_mouse_support: bool = False
187
184
  log_stdout_level: str = "CRITICAL"
188
185
 
@@ -255,8 +252,6 @@ class BaseApp(Application):
255
252
  self.dialogs.values(),
256
253
  self.menus.values(),
257
254
  )
258
- # Mapping of Containers to status field generating functions
259
- self.container_statuses: ContainerStatusDict = {}
260
255
  # Continue loading when the application has been launched
261
256
  # and an event loop has been creeated
262
257
  self.pre_run_callables = [self.pre_run]
@@ -345,16 +340,12 @@ class BaseApp(Application):
345
340
  # Set the application's style, and update it when the terminal responds
346
341
  self.update_style()
347
342
  self.term_info.colors.event += self.update_style
343
+ # Pause rendering while we load the layout
348
344
  self.pause_rendering()
349
345
  # Load completions menu. This must be done after the app is initialized, because
350
346
  # :py:func:`get_app` is needed to access the config
351
347
  self.menus["completions"] = Float(
352
- content=Shadow(
353
- CompletionsMenu(
354
- max_height=16,
355
- scroll_offset=1,
356
- )
357
- ),
348
+ content=Shadow(CompletionsMenu()),
358
349
  xcursor=True,
359
350
  ycursor=True,
360
351
  )
@@ -389,10 +380,7 @@ class BaseApp(Application):
389
380
  async def await_terminal_feedback() -> None:
390
381
  try:
391
382
  # Send queries to the terminal if supported
392
- if self.input.__class__.__name__ in (
393
- "Vt100Input",
394
- "PosixPipeInput",
395
- ):
383
+ if self.input.__class__.__name__ == "Vt100Input":
396
384
  self.term_info.send_all()
397
385
  # Give the terminal a chance to respond
398
386
  await asyncio.sleep(0.1)
@@ -421,6 +409,7 @@ class BaseApp(Application):
421
409
 
422
410
  """
423
411
  input_ = create_input(always_prefer_tty=True)
412
+
424
413
  if stdin := getattr(input_, "stdin", None):
425
414
  if not stdin.isatty():
426
415
  from euporie.core.io import IgnoredInput
@@ -515,7 +504,7 @@ class BaseApp(Application):
515
504
  def launch(cls) -> None:
516
505
  """Launch the app."""
517
506
  # Load default logging
518
- default_logs()
507
+ setup_logs()
519
508
  # Load the app's configuration
520
509
  cls.config.load(cls)
521
510
  # Configure the logs
@@ -525,7 +514,40 @@ class BaseApp(Application):
525
514
  # Run the application
526
515
  with create_app_session(input=cls.load_input(), output=cls.load_output()):
527
516
  # Create an instance of the app and run it
528
- return cls().run()
517
+
518
+ original_sigterm = signal.getsignal(signal.SIGTERM)
519
+ original_sigint = signal.getsignal(signal.SIGINT)
520
+
521
+ app = cls()
522
+
523
+ signal.signal(signal.SIGTERM, app.cleanup)
524
+ signal.signal(signal.SIGINT, app.cleanup)
525
+
526
+ result = app.run()
527
+
528
+ signal.signal(signal.SIGTERM, original_sigterm)
529
+ signal.signal(signal.SIGINT, original_sigint)
530
+
531
+ return result
532
+
533
+ def cleanup(self, signum: int, frame: FrameType | None) -> None:
534
+ """Restore the state of the terminal on unexpected exit."""
535
+ log.critical("Unexpected exit signal, restoring terminal")
536
+ output = self.output
537
+ self.exit()
538
+ # Reset terminal state
539
+ output.quit_alternate_screen()
540
+ output.disable_mouse_support()
541
+ output.reset_cursor_key_mode()
542
+ output.enable_autowrap()
543
+ output.disable_bracketed_paste()
544
+ output.clear_title()
545
+ output.reset_cursor_shape()
546
+ output.show_cursor()
547
+ output.reset_attributes()
548
+ output.flush()
549
+ # Exit the main thread
550
+ sys.exit(1)
529
551
 
530
552
  @classmethod
531
553
  async def interact(cls, ssh_session: PromptToolkitSSHSession) -> None:
@@ -544,27 +566,52 @@ class BaseApp(Application):
544
566
  floats=cast("list[Float]", self.floats),
545
567
  )
546
568
 
547
- def get_file_tab(self, path: UPath) -> type[Tab] | None:
569
+ def get_file_tabs(self, path: Path) -> list[type[Tab]]:
570
+ """Return the tab to use for a file path."""
571
+ from euporie.core.tabs.base import Tab
572
+
573
+ path_mime = get_mime(path) or "text/plain"
574
+ log.debug("File %s has mime type: %s", path, path_mime)
575
+
576
+ tab_options = set()
577
+ for tab_cls in Tab._registry:
578
+ for mime_type in tab_cls.mime_types:
579
+ if PurePath(path_mime).match(mime_type):
580
+ tab_options.add(tab_cls)
581
+ if path.suffix in tab_cls.file_extensions:
582
+ tab_options.add(tab_cls)
583
+
584
+ return sorted(tab_options, key=lambda x: x.weight, reverse=True)
585
+
586
+ def get_file_tab(self, path: Path) -> type[Tab] | None:
548
587
  """Return the tab to use for a file path."""
588
+ if tabs := self.get_file_tabs(path):
589
+ return tabs[0]
549
590
  return None
550
591
 
551
- def open_file(self, path: UPath, read_only: bool = False) -> None:
592
+ def open_file(
593
+ self, path: Path, read_only: bool = False, tab_class: type[Tab] | None = None
594
+ ) -> None:
552
595
  """Create a tab for a file.
553
596
 
554
597
  Args:
555
598
  path: The file path of the notebook file to open
556
599
  read_only: If true, the file should be opened read_only
600
+ tab_class: The tab type to use to open the file
557
601
 
558
602
  """
559
603
  ppath = parse_path(path)
560
604
  log.info(f"Opening file {path}")
561
605
  for tab in self.tabs:
562
- if ppath == getattr(tab, "path", ""):
606
+ if ppath == getattr(tab, "path", "") and (
607
+ tab_class is None or isinstance(tab, tab_class)
608
+ ):
563
609
  log.info(f"File {path} already open, activating")
564
610
  self.layout.focus(tab)
565
611
  break
566
612
  else:
567
- tab_class = self.get_file_tab(path)
613
+ if tab_class is None:
614
+ tab_class = self.get_file_tab(path)
568
615
  if tab_class is None:
569
616
  log.error("Unable to display file %s", path)
570
617
  else:
@@ -757,44 +804,6 @@ class BaseApp(Application):
757
804
  """Block default style loading."""
758
805
  return DummyStyle()
759
806
 
760
- def format_status(self, part: Literal["left", "right"]) -> StyleAndTextTuples:
761
- """Format the fields in the statusbar generated by the current tab.
762
-
763
- Args:
764
- part: ``'left'`` to return the fields on the left side of the statusbar,
765
- and ``'right'`` to return the fields on the right
766
-
767
- Returns:
768
- A list of style and text tuples for display in the statusbar
769
-
770
- """
771
- entries: StatusBarFields = ([], [])
772
- for container, status_func in self.container_statuses.items():
773
- if self.layout.has_focus(container):
774
- entries = status_func()
775
- break
776
- else:
777
- if not self.tabs:
778
- entries = self.status_default
779
-
780
- output: StyleAndTextTuples = []
781
- # Show the tab's status fields
782
- for field in entries[0 if part == "left" else 1]:
783
- if field:
784
- if isinstance(field, tuple):
785
- ft = [field]
786
- else:
787
- ft = to_formatted_text(field, style="class:status.field")
788
- output += [
789
- ("class:status.field", " "),
790
- *ft,
791
- ("class:status.field", " "),
792
- ("class:status", " "),
793
- ]
794
- if output:
795
- output.pop()
796
- return output
797
-
798
807
  def draw(self, render_as_done: bool = True) -> None:
799
808
  """Draw the app without focus, leaving the cursor below the drawn output."""
800
809
  # Hide ephemeral containers
@@ -908,45 +917,6 @@ class BaseApp(Application):
908
917
  """,
909
918
  )
910
919
 
911
- add_setting(
912
- name="log_file",
913
- flags=["--log-file"],
914
- nargs="?",
915
- default="",
916
- type_=str,
917
- title="the log file path",
918
- help_="File path for logs",
919
- description="""
920
- When set to a file path, the log output will be written to the given path.
921
- If no value is given output will be sent to the standard output.
922
- """,
923
- )
924
-
925
- add_setting(
926
- name="log_level",
927
- flags=["--log-level"],
928
- type_=str,
929
- default="",
930
- title="the log level",
931
- help_="Set the log level",
932
- choices=["debug", "info", "warning", "error", "critical"],
933
- description="""
934
- When set, logging events at the given level are emitted.
935
- """,
936
- )
937
-
938
- add_setting(
939
- name="log_config",
940
- flags=["--log-config"],
941
- type_=str,
942
- default=None,
943
- title="additional logging configuration",
944
- help_="Additional logging configuration",
945
- description="""
946
- A JSON string specifying additional logging configuration.
947
- """,
948
- )
949
-
950
920
  add_setting(
951
921
  name="edit_mode",
952
922
  flags=["--edit-mode"],
euporie/core/border.py CHANGED
@@ -275,22 +275,21 @@ ThickQuadrupleDashedLine = LineStyle("QuadDashed", (3, 1), parent=ThickLine)
275
275
  ThickTripleDashedLine = LineStyle("TripleDashed", (3, 2), parent=ThickLine)
276
276
  ThickDoubleDashedLine = LineStyle("DoubleDashed", (3, 3), parent=ThickLine)
277
277
 
278
- # ▂▂▂▂▂▂▂▂▂▂▂▂
279
- # 🮇Quarter Line▎
280
- # 🮂🮂🮂🮂🮂🮂🮂🮂🮂🮂🮂🮂
278
+ UpperRightQuarterLine = LineStyle("UpperRightQuarterLine", (4, 2), parent=ThickLine)
279
+ LowerLeftQuarterLine = LineStyle("LowerLeftQuarterLine", (4, 2), parent=ThickLine)
281
280
 
282
- UpperRightHalfLine = LineStyle("UpperRightHalfLine", (4, 2), parent=ThickLine)
283
- LowerLeftHalfLine = LineStyle("LowerLeftHalfLine", (4, 2), parent=ThickLine)
281
+ UpperRightHalfLine = LineStyle("UpperRightHalfLine", (5, 2), parent=ThickLine)
282
+ LowerLeftHalfLine = LineStyle("LowerLeftHalfLine", (5, 2), parent=ThickLine)
284
283
  UpperRightHalfDottedLine = LineStyle(
285
- "UpperRightHalfDottedLine", (4, 1), parent=UpperRightHalfLine
284
+ "UpperRightHalfDottedLine", (5, 1), parent=UpperRightHalfLine
286
285
  )
287
286
  LowerLeftHalfDottedLine = LineStyle(
288
- "LowerLeftHalfDottedLine", (4, 2), parent=LowerLeftHalfLine
287
+ "LowerLeftHalfDottedLine", (5, 2), parent=LowerLeftHalfLine
289
288
  )
290
289
 
291
290
 
292
- FullLine = LineStyle("FullLine", (5, 2), parent=ThickLine)
293
- FullDottedLine = LineStyle("FullDottedLine", (5, 1), parent=ThickLine)
291
+ FullLine = LineStyle("FullLine", (6, 2), parent=ThickLine)
292
+ FullDottedLine = LineStyle("FullDottedLine", (6, 1), parent=ThickLine)
294
293
 
295
294
 
296
295
  class GridChar(NamedTuple):
@@ -502,6 +501,37 @@ _GRID_CHARS = {
502
501
  GridChar(ThinLine, UpperRightEighthLine, NoLine, UpperRightEighthLine): "▔",
503
502
  GridChar(NoLine, NoLine, UpperRightEighthLine, LowerLeftEighthLine ): "▁",
504
503
  GridChar(LowerLeftEighthLine, UpperRightEighthLine, NoLine, NoLine ): "▔",
504
+
505
+ # UpperRightQuarterLine
506
+ GridChar(UpperRightQuarterLine, NoLine, UpperRightQuarterLine, NoLine): "🮇",
507
+ GridChar(NoLine, UpperRightQuarterLine, NoLine, UpperRightQuarterLine): "🮂",
508
+ GridChar(NoLine, UpperRightQuarterLine, UpperRightQuarterLine, NoLine): "🮇",
509
+ GridChar(UpperRightQuarterLine, NoLine, NoLine, UpperRightQuarterLine): "🮂",
510
+ GridChar(UpperRightQuarterLine, UpperRightQuarterLine, UpperRightQuarterLine, NoLine): "🮇",
511
+ GridChar(UpperRightQuarterLine, UpperRightQuarterLine, NoLine, UpperRightQuarterLine): "🮂",
512
+ # Corners
513
+ GridChar(NoLine, LowerLeftEighthLine, UpperRightQuarterLine, NoLine): " ",
514
+ GridChar(NoLine, NoLine, LowerLeftQuarterLine, LowerLeftEighthLine): " ",
515
+ GridChar(LowerLeftQuarterLine, NoLine, NoLine, UpperRightEighthLine): " ",
516
+ GridChar(UpperRightQuarterLine, UpperRightEighthLine, NoLine, NoLine): " ",
517
+
518
+ # LowerLeftQuarterLine
519
+ GridChar(LowerLeftQuarterLine, NoLine, LowerLeftQuarterLine, NoLine): "▎",
520
+ GridChar(NoLine, LowerLeftQuarterLine, NoLine, LowerLeftQuarterLine): "▂",
521
+ GridChar(NoLine, LowerLeftQuarterLine, LowerLeftQuarterLine, NoLine): "▂",
522
+ GridChar(LowerLeftQuarterLine, NoLine, NoLine, LowerLeftQuarterLine): "▎",
523
+ GridChar(NoLine, LowerLeftQuarterLine, LowerLeftQuarterLine, LowerLeftQuarterLine): "▂",
524
+ GridChar(LowerLeftQuarterLine, NoLine, LowerLeftQuarterLine, LowerLeftQuarterLine): "▎",
525
+ # Half/ThinLine combos
526
+ GridChar(LowerLeftQuarterLine, ThinLine, LowerLeftQuarterLine, NoLine): "▎",
527
+ GridChar(NoLine, LowerLeftQuarterLine, ThinLine, LowerLeftQuarterLine): "▂",
528
+ GridChar(LowerLeftQuarterLine, NoLine, LowerLeftQuarterLine, ThinLine): "▎",
529
+ GridChar(ThinLine, LowerLeftQuarterLine, NoLine, LowerLeftQuarterLine): "▂",
530
+ GridChar(UpperRightQuarterLine, ThinLine, UpperRightQuarterLine, NoLine): "🮇",
531
+ GridChar(NoLine, UpperRightQuarterLine, ThinLine, UpperRightQuarterLine): "🮂",
532
+ GridChar(UpperRightQuarterLine, NoLine, UpperRightQuarterLine, ThinLine): "🮇",
533
+ GridChar(ThinLine, UpperRightQuarterLine, NoLine, UpperRightQuarterLine): "🮂",
534
+
505
535
  # UpperRightHalfLine
506
536
  GridChar(UpperRightHalfLine, NoLine, UpperRightHalfLine, NoLine): "▐",
507
537
  GridChar(NoLine, UpperRightHalfLine, NoLine, UpperRightHalfLine): "▀",
@@ -817,15 +847,15 @@ class GridStyle:
817
847
 
818
848
  ThinGrid = ThinLine.grid
819
849
 
820
- InnerEigthGrid = (
821
- LowerLeftEighthLine.top_edge
822
- + LowerLeftEighthLine.right_edge
823
- + UpperRightEighthLine.left_edge
850
+ InsetGrid = (
851
+ UpperRightQuarterLine.left_edge
852
+ + LowerLeftQuarterLine.right_edge
824
853
  + UpperRightEighthLine.bottom_edge
854
+ + LowerLeftEighthLine.top_edge
825
855
  + ThinLine.inner
826
856
  )
827
857
 
828
- OuterEigthGrid = (
858
+ OutsetGrid = (
829
859
  LowerLeftEighthLine.top_edge
830
860
  + UpperRightEighthLine.right_edge
831
861
  + UpperRightEighthLine.bottom_edge
euporie/core/comm/base.py CHANGED
@@ -11,7 +11,7 @@ from euporie.core.current import get_app
11
11
  from euporie.core.widgets.display import Display
12
12
 
13
13
  if TYPE_CHECKING:
14
- from typing import Any, Callable, Sequence
14
+ from typing import Any, Callable, Mapping, Sequence
15
15
 
16
16
  from prompt_toolkit.layout.containers import AnyContainer
17
17
 
@@ -29,7 +29,7 @@ class CommView:
29
29
  def __init__(
30
30
  self,
31
31
  container: AnyContainer,
32
- setters: dict[str, Callable[..., None]] | None = None,
32
+ setters: Mapping[str, Callable[..., None]] | None = None,
33
33
  ) -> None:
34
34
  """Create a new instance of the Comm vieew.
35
35
 
@@ -39,7 +39,7 @@ class CommView:
39
39
  be called when the named state value changed.
40
40
  """
41
41
  self.container = container
42
- self.setters = setters or {}
42
+ self.setters: dict[str, Callable[..., None]] = dict(setters or {})
43
43
  self.kernel: Kernel | None = None
44
44
 
45
45
  def update(self, changes: dict[str, Any]) -> None:
@@ -117,4 +117,5 @@ class UnimplementedComm(Comm):
117
117
 
118
118
  def process_data(self, data: dict, buffers: Sequence[bytes]) -> None:
119
119
  """Doe nothing when data is received."""
120
- return None
120
+ self.data = data
121
+ self.buffers = buffers