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.
- euporie/console/__main__.py +3 -1
- euporie/console/app.py +6 -4
- euporie/console/tabs/console.py +34 -9
- euporie/core/__init__.py +6 -1
- euporie/core/__main__.py +1 -1
- euporie/core/app.py +79 -109
- euporie/core/border.py +44 -14
- euporie/core/comm/base.py +5 -4
- euporie/core/comm/ipywidgets.py +11 -11
- euporie/core/comm/registry.py +12 -6
- euporie/core/commands.py +30 -23
- euporie/core/completion.py +1 -4
- euporie/core/config.py +15 -5
- euporie/core/convert/{base.py → core.py} +117 -53
- euporie/core/convert/formats/ansi.py +46 -25
- euporie/core/convert/formats/base64.py +3 -3
- euporie/core/convert/formats/common.py +38 -13
- euporie/core/convert/formats/formatted_text.py +54 -12
- euporie/core/convert/formats/html.py +5 -5
- euporie/core/convert/formats/jpeg.py +1 -1
- euporie/core/convert/formats/markdown.py +4 -4
- euporie/core/convert/formats/pdf.py +1 -1
- euporie/core/convert/formats/pil.py +5 -3
- euporie/core/convert/formats/png.py +7 -6
- euporie/core/convert/formats/rich.py +4 -3
- euporie/core/convert/formats/sixel.py +5 -5
- euporie/core/convert/utils.py +1 -1
- euporie/core/current.py +11 -5
- euporie/core/formatted_text/ansi.py +4 -8
- euporie/core/formatted_text/html.py +1630 -856
- euporie/core/formatted_text/markdown.py +177 -166
- euporie/core/formatted_text/table.py +20 -14
- euporie/core/formatted_text/utils.py +21 -10
- euporie/core/io.py +14 -14
- euporie/core/kernel.py +48 -37
- euporie/core/key_binding/bindings/micro.py +5 -1
- euporie/core/key_binding/bindings/mouse.py +2 -2
- euporie/core/keys.py +3 -0
- euporie/core/launch.py +5 -2
- euporie/core/lexers.py +13 -2
- euporie/core/log.py +135 -139
- euporie/core/margins.py +32 -14
- euporie/core/path.py +273 -0
- euporie/core/processors.py +35 -0
- euporie/core/renderer.py +21 -5
- euporie/core/style.py +34 -19
- euporie/core/tabs/base.py +101 -17
- euporie/core/tabs/notebook.py +72 -30
- euporie/core/terminal.py +56 -48
- euporie/core/utils.py +12 -16
- euporie/core/widgets/cell.py +6 -5
- euporie/core/widgets/cell_outputs.py +2 -2
- euporie/core/widgets/decor.py +74 -82
- euporie/core/widgets/dialog.py +132 -28
- euporie/core/widgets/display.py +76 -24
- euporie/core/widgets/file_browser.py +87 -31
- euporie/core/widgets/formatted_text_area.py +1 -3
- euporie/core/widgets/forms.py +79 -40
- euporie/core/widgets/inputs.py +23 -13
- euporie/core/widgets/layout.py +4 -3
- euporie/core/widgets/menu.py +368 -216
- euporie/core/widgets/page.py +99 -58
- euporie/core/widgets/pager.py +1 -1
- euporie/core/widgets/palette.py +30 -27
- euporie/core/widgets/search_bar.py +38 -25
- euporie/core/widgets/status_bar.py +103 -5
- euporie/data/desktop/euporie-console.desktop +7 -0
- euporie/data/desktop/euporie-notebook.desktop +7 -0
- euporie/hub/__main__.py +3 -1
- euporie/hub/app.py +9 -7
- euporie/notebook/__main__.py +3 -1
- euporie/notebook/app.py +7 -30
- euporie/notebook/tabs/__init__.py +7 -3
- euporie/notebook/tabs/display.py +18 -9
- euporie/notebook/tabs/edit.py +106 -23
- euporie/notebook/tabs/json.py +73 -0
- euporie/notebook/tabs/log.py +18 -8
- euporie/notebook/tabs/notebook.py +60 -41
- euporie/preview/__main__.py +3 -1
- euporie/preview/app.py +2 -1
- euporie/preview/tabs/notebook.py +23 -10
- euporie/web/tabs/web.py +149 -0
- euporie/web/widgets/webview.py +563 -0
- euporie-2.4.1.data/data/share/applications/euporie-console.desktop +7 -0
- euporie-2.4.1.data/data/share/applications/euporie-notebook.desktop +7 -0
- {euporie-2.3.2.dist-info → euporie-2.4.1.dist-info}/METADATA +6 -5
- euporie-2.4.1.dist-info/RECORD +129 -0
- {euporie-2.3.2.dist-info → euporie-2.4.1.dist-info}/WHEEL +1 -1
- euporie/core/url.py +0 -64
- euporie-2.3.2.dist-info/RECORD +0 -122
- {euporie-2.3.2.dist-info → euporie-2.4.1.dist-info}/entry_points.txt +0 -0
- {euporie-2.3.2.dist-info → euporie-2.4.1.dist-info}/licenses/LICENSE +0 -0
euporie/core/widgets/decor.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
"""Decorative widgets."""
|
2
2
|
|
3
|
-
|
3
|
+
from __future__ import annotations
|
4
4
|
|
5
5
|
import logging
|
6
6
|
from typing import TYPE_CHECKING
|
@@ -30,7 +30,7 @@ from euporie.core.data_structures import DiBool
|
|
30
30
|
from euporie.core.style import ColorPaletteColor
|
31
31
|
|
32
32
|
if TYPE_CHECKING:
|
33
|
-
from typing import Callable
|
33
|
+
from typing import Callable
|
34
34
|
|
35
35
|
from prompt_toolkit.key_binding.key_bindings import NotImplementedOrNone
|
36
36
|
from prompt_toolkit.layout.containers import AnyContainer
|
@@ -48,12 +48,12 @@ class Line(Container):
|
|
48
48
|
|
49
49
|
def __init__(
|
50
50
|
self,
|
51
|
-
char:
|
52
|
-
width:
|
53
|
-
height:
|
54
|
-
collapse:
|
55
|
-
style:
|
56
|
-
) ->
|
51
|
+
char: str | None = None,
|
52
|
+
width: int | None = None,
|
53
|
+
height: int | None = None,
|
54
|
+
collapse: bool = False,
|
55
|
+
style: str = "class:grid-line",
|
56
|
+
) -> None:
|
57
57
|
"""Initialize a grid line.
|
58
58
|
|
59
59
|
Args:
|
@@ -78,28 +78,26 @@ class Line(Container):
|
|
78
78
|
self.char = Char(char, style)
|
79
79
|
self.collapse = collapse
|
80
80
|
|
81
|
-
def reset(self) ->
|
81
|
+
def reset(self) -> None:
|
82
82
|
"""Reet the state of the line. Does nothing."""
|
83
83
|
|
84
|
-
def preferred_width(self, max_available_width:
|
84
|
+
def preferred_width(self, max_available_width: int) -> Dimension:
|
85
85
|
"""Return the preferred width of the line."""
|
86
86
|
return Dimension(min=int(not self.collapse), max=self.width)
|
87
87
|
|
88
|
-
def preferred_height(
|
89
|
-
self, width: "int", max_available_height: "int"
|
90
|
-
) -> "Dimension":
|
88
|
+
def preferred_height(self, width: int, max_available_height: int) -> Dimension:
|
91
89
|
"""Return the preferred height of the line."""
|
92
90
|
return Dimension(min=int(not self.collapse), max=self.height)
|
93
91
|
|
94
92
|
def write_to_screen(
|
95
93
|
self,
|
96
|
-
screen:
|
97
|
-
mouse_handlers:
|
98
|
-
write_position:
|
99
|
-
parent_style:
|
100
|
-
erase_bg:
|
101
|
-
z_index:
|
102
|
-
) ->
|
94
|
+
screen: Screen,
|
95
|
+
mouse_handlers: MouseHandlers,
|
96
|
+
write_position: WritePosition,
|
97
|
+
parent_style: str,
|
98
|
+
erase_bg: bool,
|
99
|
+
z_index: int | None,
|
100
|
+
) -> None:
|
103
101
|
"""Draw a continuous line in the ``write_position`` area.
|
104
102
|
|
105
103
|
Args:
|
@@ -124,7 +122,7 @@ class Line(Container):
|
|
124
122
|
for x in range(xpos, xpos + write_position.width):
|
125
123
|
row[x] = self.char
|
126
124
|
|
127
|
-
def get_children(self) ->
|
125
|
+
def get_children(self) -> list:
|
128
126
|
"""Return an empty list of the container's children."""
|
129
127
|
return []
|
130
128
|
|
@@ -134,37 +132,35 @@ class Pattern(Container):
|
|
134
132
|
|
135
133
|
def __init__(
|
136
134
|
self,
|
137
|
-
char:
|
138
|
-
pattern:
|
139
|
-
) ->
|
135
|
+
char: str | Callable[[], str],
|
136
|
+
pattern: int | Callable[[], int] = 1,
|
137
|
+
) -> None:
|
140
138
|
"""Initialize the :class:`Pattern`."""
|
141
139
|
self.bg = Char(" ", "class:pattern")
|
142
140
|
self.char = char
|
143
141
|
self.pattern = pattern
|
144
142
|
|
145
|
-
def reset(self) ->
|
143
|
+
def reset(self) -> None:
|
146
144
|
"""Reet the pattern. Does nothing."""
|
147
145
|
pass
|
148
146
|
|
149
|
-
def preferred_width(self, max_available_width:
|
147
|
+
def preferred_width(self, max_available_width: int) -> Dimension:
|
150
148
|
"""Return an empty dimension (expand to available width)."""
|
151
149
|
return Dimension()
|
152
150
|
|
153
|
-
def preferred_height(
|
154
|
-
self, width: "int", max_available_height: "int"
|
155
|
-
) -> "Dimension":
|
151
|
+
def preferred_height(self, width: int, max_available_height: int) -> Dimension:
|
156
152
|
"""Return an empty dimension (expand to available height)."""
|
157
153
|
return Dimension()
|
158
154
|
|
159
155
|
def write_to_screen(
|
160
156
|
self,
|
161
|
-
screen:
|
162
|
-
mouse_handlers:
|
163
|
-
write_position:
|
164
|
-
parent_style:
|
165
|
-
erase_bg:
|
166
|
-
z_index:
|
167
|
-
) ->
|
157
|
+
screen: Screen,
|
158
|
+
mouse_handlers: MouseHandlers,
|
159
|
+
write_position: WritePosition,
|
160
|
+
parent_style: str,
|
161
|
+
erase_bg: bool,
|
162
|
+
z_index: int | None,
|
163
|
+
) -> None:
|
168
164
|
"""Fill the whole area of write_position with a pattern.
|
169
165
|
|
170
166
|
Args:
|
@@ -208,7 +204,7 @@ class Pattern(Container):
|
|
208
204
|
else:
|
209
205
|
row[x] = bg
|
210
206
|
|
211
|
-
def get_children(self) ->
|
207
|
+
def get_children(self) -> list:
|
212
208
|
"""Return an empty list of the container's children."""
|
213
209
|
return []
|
214
210
|
|
@@ -218,11 +214,11 @@ class Border:
|
|
218
214
|
|
219
215
|
def __init__(
|
220
216
|
self,
|
221
|
-
body:
|
222
|
-
border:
|
223
|
-
style:
|
224
|
-
show_borders:
|
225
|
-
) ->
|
217
|
+
body: AnyContainer,
|
218
|
+
border: GridStyle | None = ThinLine.grid,
|
219
|
+
style: str | Callable[[], str] = "class:border",
|
220
|
+
show_borders: DiBool | None = None,
|
221
|
+
) -> None:
|
226
222
|
"""Create a new border widget which wraps another container.
|
227
223
|
|
228
224
|
Args:
|
@@ -244,7 +240,7 @@ class Border:
|
|
244
240
|
border_bottom = to_filter(show_borders.bottom)
|
245
241
|
border_left = to_filter(show_borders.left)
|
246
242
|
|
247
|
-
self.container:
|
243
|
+
self.container: AnyContainer
|
248
244
|
if border is not None and any(show_borders):
|
249
245
|
self.container = HSplit(
|
250
246
|
[
|
@@ -344,10 +340,10 @@ class Border:
|
|
344
340
|
else:
|
345
341
|
self.container = body
|
346
342
|
|
347
|
-
def add_style(self, extra:
|
343
|
+
def add_style(self, extra: str) -> Callable[[], str]:
|
348
344
|
"""Return a function which adds a style string to the border style."""
|
349
345
|
|
350
|
-
def _style() ->
|
346
|
+
def _style() -> str:
|
351
347
|
if callable(self.style):
|
352
348
|
return f"{self.style()} {extra}"
|
353
349
|
else:
|
@@ -355,7 +351,7 @@ class Border:
|
|
355
351
|
|
356
352
|
return _style
|
357
353
|
|
358
|
-
def __pt_container__(self) ->
|
354
|
+
def __pt_container__(self) -> AnyContainer:
|
359
355
|
"""Return the border widget's container."""
|
360
356
|
return self.container
|
361
357
|
|
@@ -365,10 +361,10 @@ class FocusedStyle(Container):
|
|
365
361
|
|
366
362
|
def __init__(
|
367
363
|
self,
|
368
|
-
body:
|
369
|
-
style_focus:
|
370
|
-
style_hover:
|
371
|
-
) ->
|
364
|
+
body: AnyContainer,
|
365
|
+
style_focus: str | Callable[[], str] = "class:focused",
|
366
|
+
style_hover: str | Callable[[], str] = "",
|
367
|
+
) -> None:
|
372
368
|
"""Create a new instance of the widget.
|
373
369
|
|
374
370
|
Args:
|
@@ -382,29 +378,27 @@ class FocusedStyle(Container):
|
|
382
378
|
self.hover = False
|
383
379
|
self.has_focus = has_focus(self.body)
|
384
380
|
|
385
|
-
def reset(self) ->
|
381
|
+
def reset(self) -> None:
|
386
382
|
"""Reet the wrapped container."""
|
387
383
|
to_container(self.body).reset()
|
388
384
|
|
389
|
-
def preferred_width(self, max_available_width:
|
385
|
+
def preferred_width(self, max_available_width: int) -> Dimension:
|
390
386
|
"""Return the wrapped container's preferred width."""
|
391
387
|
return to_container(self.body).preferred_width(max_available_width)
|
392
388
|
|
393
|
-
def preferred_height(
|
394
|
-
self, width: "int", max_available_height: "int"
|
395
|
-
) -> "Dimension":
|
389
|
+
def preferred_height(self, width: int, max_available_height: int) -> Dimension:
|
396
390
|
"""Return the wrapped container's preferred height."""
|
397
391
|
return to_container(self.body).preferred_height(width, max_available_height)
|
398
392
|
|
399
393
|
def write_to_screen(
|
400
394
|
self,
|
401
|
-
screen:
|
402
|
-
mouse_handlers:
|
403
|
-
write_position:
|
404
|
-
parent_style:
|
405
|
-
erase_bg:
|
406
|
-
z_index:
|
407
|
-
) ->
|
395
|
+
screen: Screen,
|
396
|
+
mouse_handlers: MouseHandlers,
|
397
|
+
write_position: WritePosition,
|
398
|
+
parent_style: str,
|
399
|
+
erase_bg: bool,
|
400
|
+
z_index: int | None,
|
401
|
+
) -> None:
|
408
402
|
"""Draw the wrapped container with the additional style."""
|
409
403
|
to_container(self.body).write_to_screen(
|
410
404
|
screen,
|
@@ -422,10 +416,10 @@ class FocusedStyle(Container):
|
|
422
416
|
y_max = y_min + write_position.height
|
423
417
|
|
424
418
|
# Wrap mouse handlers to add "hover" class on hover
|
425
|
-
def _wrap_mouse_handler(handler:
|
419
|
+
def _wrap_mouse_handler(handler: Callable) -> MouseHandler:
|
426
420
|
def wrapped_mouse_handler(
|
427
|
-
mouse_event:
|
428
|
-
) ->
|
421
|
+
mouse_event: MouseEvent,
|
422
|
+
) -> NotImplementedOrNone:
|
429
423
|
result = handler(mouse_event)
|
430
424
|
|
431
425
|
if mouse_event.event_type == MouseEventType.MOUSE_MOVE:
|
@@ -452,7 +446,7 @@ class FocusedStyle(Container):
|
|
452
446
|
for x in range(x_min, x_max):
|
453
447
|
row[x] = mouse_handler_wrappers[(row[x],)]
|
454
448
|
|
455
|
-
def get_style(self) ->
|
449
|
+
def get_style(self) -> str:
|
456
450
|
"""Determine the style to apply depending on the focus status."""
|
457
451
|
style = ""
|
458
452
|
if self.has_focus():
|
@@ -465,7 +459,7 @@ class FocusedStyle(Container):
|
|
465
459
|
)
|
466
460
|
return style
|
467
461
|
|
468
|
-
def get_children(self) ->
|
462
|
+
def get_children(self) -> list[Container]:
|
469
463
|
"""Return the list of child :class:`.Container` objects."""
|
470
464
|
return [to_container(self.body)]
|
471
465
|
|
@@ -480,28 +474,26 @@ class DropShadow(Container):
|
|
480
474
|
self.cp = app.color_palette
|
481
475
|
self.renderer = app.renderer
|
482
476
|
|
483
|
-
def reset(self) ->
|
477
|
+
def reset(self) -> None:
|
484
478
|
"""Reet the wrapped container - here, do nothing."""
|
485
479
|
|
486
|
-
def preferred_width(self, max_available_width:
|
480
|
+
def preferred_width(self, max_available_width: int) -> Dimension:
|
487
481
|
"""Return the wrapped container's preferred width."""
|
488
482
|
return Dimension(weight=1)
|
489
483
|
|
490
|
-
def preferred_height(
|
491
|
-
self, width: "int", max_available_height: "int"
|
492
|
-
) -> "Dimension":
|
484
|
+
def preferred_height(self, width: int, max_available_height: int) -> Dimension:
|
493
485
|
"""Return the wrapped container's preferred height."""
|
494
486
|
return Dimension(weight=1)
|
495
487
|
|
496
488
|
def write_to_screen(
|
497
489
|
self,
|
498
|
-
screen:
|
499
|
-
mouse_handlers:
|
500
|
-
write_position:
|
501
|
-
parent_style:
|
502
|
-
erase_bg:
|
503
|
-
z_index:
|
504
|
-
) ->
|
490
|
+
screen: Screen,
|
491
|
+
mouse_handlers: MouseHandlers,
|
492
|
+
write_position: WritePosition,
|
493
|
+
parent_style: str,
|
494
|
+
erase_bg: bool,
|
495
|
+
z_index: int | None,
|
496
|
+
) -> None:
|
505
497
|
"""Draw the wrapped container with the additional style."""
|
506
498
|
attr_cache = self.renderer._attrs_for_style
|
507
499
|
if attr_cache is not None:
|
@@ -551,7 +543,7 @@ class Shadow:
|
|
551
543
|
:py:class:`prompt_toolkit.widows.base.Shadow` class.
|
552
544
|
"""
|
553
545
|
|
554
|
-
def __init__(self, body:
|
546
|
+
def __init__(self, body: AnyContainer) -> None:
|
555
547
|
"""Initialize a new drop-shadow container.
|
556
548
|
|
557
549
|
Args:
|
@@ -580,7 +572,7 @@ class Shadow:
|
|
580
572
|
],
|
581
573
|
)
|
582
574
|
|
583
|
-
def get_contents() ->
|
575
|
+
def get_contents() -> AnyContainer:
|
584
576
|
if filter_():
|
585
577
|
return shadow
|
586
578
|
else:
|
@@ -588,7 +580,7 @@ class Shadow:
|
|
588
580
|
|
589
581
|
self.container = DynamicContainer(get_contents)
|
590
582
|
|
591
|
-
def __pt_container__(self) ->
|
583
|
+
def __pt_container__(self) -> AnyContainer:
|
592
584
|
"""Return the container's content."""
|
593
585
|
return self.container
|
594
586
|
|
euporie/core/widgets/dialog.py
CHANGED
@@ -6,13 +6,19 @@ import logging
|
|
6
6
|
import traceback
|
7
7
|
from abc import ABCMeta, abstractmethod
|
8
8
|
from functools import partial
|
9
|
+
from pathlib import Path
|
9
10
|
from typing import TYPE_CHECKING
|
10
11
|
|
11
12
|
from prompt_toolkit.cache import SimpleCache
|
12
13
|
from prompt_toolkit.clipboard import ClipboardData
|
13
14
|
from prompt_toolkit.completion import PathCompleter
|
14
15
|
from prompt_toolkit.data_structures import Point
|
15
|
-
from prompt_toolkit.filters import
|
16
|
+
from prompt_toolkit.filters import (
|
17
|
+
Condition,
|
18
|
+
buffer_has_focus,
|
19
|
+
has_completions,
|
20
|
+
has_focus,
|
21
|
+
)
|
16
22
|
from prompt_toolkit.formatted_text import AnyFormattedText, to_formatted_text
|
17
23
|
from prompt_toolkit.formatted_text.utils import split_lines
|
18
24
|
from prompt_toolkit.key_binding.bindings.focus import focus_next, focus_previous
|
@@ -31,6 +37,7 @@ from prompt_toolkit.layout.screen import WritePosition
|
|
31
37
|
from prompt_toolkit.mouse_events import MouseButton, MouseEventType
|
32
38
|
from prompt_toolkit.widgets.base import Box, Label
|
33
39
|
|
40
|
+
from euporie.core.app import get_app
|
34
41
|
from euporie.core.border import (
|
35
42
|
FullLine,
|
36
43
|
LowerLeftHalfLine,
|
@@ -58,6 +65,7 @@ if TYPE_CHECKING:
|
|
58
65
|
|
59
66
|
from euporie.core.app import BaseApp
|
60
67
|
from euporie.core.tabs.base import KernelTab
|
68
|
+
from euporie.core.widgets.file_browser import FileBrowserControl
|
61
69
|
|
62
70
|
log = logging.getLogger(__name__)
|
63
71
|
|
@@ -184,14 +192,23 @@ class Dialog(Float, metaclass=ABCMeta):
|
|
184
192
|
self.button_widgets: list[AnyContainer] = []
|
185
193
|
|
186
194
|
# Create key-bindings
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
195
|
+
kb = KeyBindings()
|
196
|
+
kb.add("escape")(lambda event: self.hide())
|
197
|
+
kb.add("tab", filter=~has_completions)(focus_next)
|
198
|
+
kb.add("s-tab", filter=~has_completions)(focus_previous)
|
199
|
+
|
200
|
+
@kb.add("enter", filter=~has_completions & ~buffer_has_focus)
|
201
|
+
def _focus_button(event: KeyPressEvent) -> NotImplementedOrNone:
|
202
|
+
if self.button_widgets:
|
203
|
+
app.layout.focus(self.button_widgets[0])
|
204
|
+
return None
|
205
|
+
return NotImplemented
|
206
|
+
|
207
|
+
self.kb = kb
|
191
208
|
self.buttons_kb = KeyBindings()
|
192
209
|
|
193
210
|
# Create title row
|
194
|
-
title_window = Window(height=1, style="class:dialog,title")
|
211
|
+
title_window = Window(height=1, style="class:dialog,dialog-title")
|
195
212
|
title_window.content = DialogTitleControl(
|
196
213
|
lambda: self.title, self, title_window
|
197
214
|
)
|
@@ -234,7 +251,7 @@ class Dialog(Float, metaclass=ABCMeta):
|
|
234
251
|
],
|
235
252
|
style="class:dialog,body",
|
236
253
|
key_bindings=self.kb,
|
237
|
-
modal=
|
254
|
+
modal=False,
|
238
255
|
),
|
239
256
|
border=DialogGrid,
|
240
257
|
style="class:dialog,border",
|
@@ -452,6 +469,7 @@ class FileDialog(Dialog, metaclass=ABCMeta):
|
|
452
469
|
self.file_browser.control.on_open += lambda path: self.validate(
|
453
470
|
self.filepath.buffer, tab=tab, cb=cb
|
454
471
|
)
|
472
|
+
self.file_browser.control.selected = None
|
455
473
|
self.error = error
|
456
474
|
self.to_focus = self.filepath
|
457
475
|
|
@@ -467,23 +485,65 @@ class OpenFileDialog(FileDialog):
|
|
467
485
|
|
468
486
|
title = "Select a File to Open"
|
469
487
|
|
488
|
+
def __init__(self, app: BaseApp) -> None:
|
489
|
+
"""Additional body components."""
|
490
|
+
from euporie.core.widgets.forms import Dropdown
|
491
|
+
|
492
|
+
super().__init__(app)
|
493
|
+
|
494
|
+
self.tab_dd = tab_dd = Dropdown(options=[])
|
495
|
+
|
496
|
+
def _update_options(fb: FileBrowserControl) -> None:
|
497
|
+
tabs = get_app().get_file_tabs(path) if (path := fb.path).is_file() else []
|
498
|
+
tab_dd.options = tabs
|
499
|
+
tab_dd.labels = [tab.name for tab in tabs]
|
500
|
+
|
501
|
+
self.file_browser.control.on_select += _update_options
|
502
|
+
|
503
|
+
if isinstance(self.body, HSplit):
|
504
|
+
self.body.children.append(
|
505
|
+
ConditionalContainer(
|
506
|
+
FocusedStyle(LabelledWidget(tab_dd, "Open with:")),
|
507
|
+
filter=Condition(lambda: len(tab_dd.options) > 1),
|
508
|
+
)
|
509
|
+
)
|
510
|
+
|
511
|
+
def load(
|
512
|
+
self,
|
513
|
+
text: str = "",
|
514
|
+
tab: Tab | None = None,
|
515
|
+
error: str = "",
|
516
|
+
cb: Callable | None = None,
|
517
|
+
) -> None:
|
518
|
+
"""Load the dialog body."""
|
519
|
+
super().load(text=text, tab=tab, error=error, cb=cb)
|
520
|
+
self.tab_dd.options = []
|
521
|
+
self.tab_dd.labels = []
|
522
|
+
|
470
523
|
def validate(
|
471
524
|
self, buffer: Buffer, tab: Tab | None, cb: Callable | None = None
|
472
525
|
) -> None:
|
473
526
|
"""Validate the the file to open exists."""
|
474
|
-
from
|
527
|
+
from upath import UPath
|
528
|
+
|
529
|
+
from euporie.core.path import parse_path
|
475
530
|
|
476
531
|
path = parse_path(self.file_browser.control.dir / buffer.text)
|
477
532
|
if path is not None:
|
478
|
-
if
|
479
|
-
|
480
|
-
|
481
|
-
|
533
|
+
if not path.exists():
|
534
|
+
path = UPath(buffer.text)
|
535
|
+
|
536
|
+
if path.exists():
|
537
|
+
if path.is_dir():
|
538
|
+
self.file_browser.control.dir = path
|
539
|
+
elif path.is_file():
|
540
|
+
self.hide()
|
541
|
+
self.app.open_file(path, tab_class=self.tab_dd.value)
|
542
|
+
return
|
543
|
+
else:
|
482
544
|
self.show(
|
483
545
|
error="The file path specified does not exist", text=buffer.text
|
484
546
|
)
|
485
|
-
elif path.is_dir():
|
486
|
-
self.file_browser.control.dir = path
|
487
547
|
|
488
548
|
# ################################### Commands ####################################
|
489
549
|
|
@@ -516,7 +576,7 @@ class SaveAsDialog(FileDialog):
|
|
516
576
|
self, buffer: Buffer, tab: Tab | None, cb: Callable | None = None
|
517
577
|
) -> None:
|
518
578
|
"""Validate the the file to open exists."""
|
519
|
-
from euporie.core.
|
579
|
+
from euporie.core.path import parse_path
|
520
580
|
|
521
581
|
path = parse_path(self.file_browser.control.dir / buffer.text)
|
522
582
|
if tab and path is not None:
|
@@ -589,13 +649,19 @@ class SelectKernelDialog(Dialog):
|
|
589
649
|
def load(
|
590
650
|
self,
|
591
651
|
kernel_specs: dict[str, Any] | None = None,
|
652
|
+
runtime_dirs: dict[str, Path] | None = None,
|
592
653
|
tab: KernelTab | None = None,
|
593
654
|
message: str = "",
|
594
655
|
) -> None:
|
595
656
|
"""Load dialog body & buttons."""
|
657
|
+
from jupyter_core.paths import jupyter_runtime_dir
|
658
|
+
|
659
|
+
from euporie.core.widgets.layout import TabbedSplit
|
660
|
+
|
596
661
|
kernel_specs = kernel_specs or {}
|
662
|
+
runtime_dirs = runtime_dirs or {}
|
597
663
|
|
598
|
-
|
664
|
+
options_specs = Select(
|
599
665
|
options=list(kernel_specs.keys()),
|
600
666
|
labels=[
|
601
667
|
kernel_spec.get("spec", {}).get("display_name", kernel_name)
|
@@ -608,21 +674,51 @@ class SelectKernelDialog(Dialog):
|
|
608
674
|
rows=5,
|
609
675
|
dont_extend_width=False,
|
610
676
|
)
|
677
|
+
self.to_focus = options_specs
|
678
|
+
|
679
|
+
connection_files = {
|
680
|
+
path.name: path
|
681
|
+
for path in Path(jupyter_runtime_dir()).glob("kernel-*.json")
|
682
|
+
}
|
683
|
+
options_files = Select(
|
684
|
+
options=list(connection_files.values()),
|
685
|
+
labels=list(connection_files.keys()),
|
686
|
+
style="class:input,radio-buttons",
|
687
|
+
prefix=("○", "◉"),
|
688
|
+
multiple=False,
|
689
|
+
border=None,
|
690
|
+
rows=5,
|
691
|
+
dont_extend_width=False,
|
692
|
+
)
|
611
693
|
|
612
|
-
msg_ft = (f"{message}\n" if message else "") + "Please select a kernel
|
694
|
+
msg_ft = (f"{message}\n\n" if message else "") + "Please select a kernel:"
|
613
695
|
|
614
696
|
self.body = HSplit(
|
615
697
|
[
|
616
698
|
Label(msg_ft),
|
617
|
-
|
699
|
+
tabs := TabbedSplit(
|
700
|
+
[
|
701
|
+
FocusedStyle(options_specs),
|
702
|
+
FocusedStyle(options_files),
|
703
|
+
],
|
704
|
+
titles=["New", "Existing"],
|
705
|
+
width=Dimension(min=30),
|
706
|
+
),
|
618
707
|
]
|
619
708
|
)
|
620
709
|
|
621
710
|
def _change_kernel() -> None:
|
622
711
|
self.hide()
|
623
712
|
assert tab is not None
|
624
|
-
|
625
|
-
|
713
|
+
if tabs.active == 0:
|
714
|
+
name = options_specs.value
|
715
|
+
connection_file = None
|
716
|
+
else:
|
717
|
+
name = None
|
718
|
+
connection_file = options_files.value
|
719
|
+
tab.kernel.change(
|
720
|
+
name=name, connection_file=connection_file, cb=tab.kernel_started
|
721
|
+
)
|
626
722
|
|
627
723
|
self.buttons = {
|
628
724
|
"Select": _change_kernel,
|
@@ -672,6 +768,7 @@ class ErrorDialog(Dialog):
|
|
672
768
|
|
673
769
|
def load(self, exception: Exception | None = None, when: str = "") -> None:
|
674
770
|
"""Load dialog body & buttons."""
|
771
|
+
from euporie.core.margins import MarginContainer, ScrollbarMargin
|
675
772
|
from euporie.core.widgets.formatted_text_area import FormattedTextArea
|
676
773
|
from euporie.core.widgets.forms import Checkbox
|
677
774
|
|
@@ -705,12 +802,17 @@ class ErrorDialog(Dialog):
|
|
705
802
|
Box(checkbox, padding_left=0),
|
706
803
|
),
|
707
804
|
ConditionalContainer(
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
805
|
+
VSplit(
|
806
|
+
[
|
807
|
+
fta := FormattedTextArea(
|
808
|
+
lex([("", tb_text)], "pytb"),
|
809
|
+
width=80,
|
810
|
+
height=Dimension(min=10),
|
811
|
+
wrap_lines=False,
|
812
|
+
style="",
|
813
|
+
),
|
814
|
+
MarginContainer(ScrollbarMargin(), target=fta.window),
|
815
|
+
]
|
714
816
|
),
|
715
817
|
filter=Condition(lambda: checkbox.selected),
|
716
818
|
),
|
@@ -775,6 +877,7 @@ class ShortcutsDialog(Dialog):
|
|
775
877
|
def load(self, *args: Any, **kwargs: Any) -> None:
|
776
878
|
"""Load the dialog body."""
|
777
879
|
from euporie.core.formatted_text.utils import max_line_width
|
880
|
+
from euporie.core.margins import MarginContainer, ScrollbarMargin
|
778
881
|
from euporie.core.widgets.formatted_text_area import FormattedTextArea
|
779
882
|
|
780
883
|
if not self.details:
|
@@ -783,13 +886,14 @@ class ShortcutsDialog(Dialog):
|
|
783
886
|
|
784
887
|
width = max_line_width(self.details)
|
785
888
|
|
786
|
-
|
889
|
+
fta = FormattedTextArea(
|
787
890
|
formatted_text=self.details,
|
788
891
|
multiline=True,
|
789
892
|
focusable=True,
|
790
893
|
wrap_lines=False,
|
791
|
-
width=width,
|
894
|
+
width=width - 2,
|
792
895
|
)
|
896
|
+
self.body = VSplit([fta, MarginContainer(ScrollbarMargin(), target=fta.window)])
|
793
897
|
|
794
898
|
def format_key_info(self) -> StyleAndTextTuples:
|
795
899
|
"""Generate a table with the current key bindings."""
|