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/forms.py
CHANGED
@@ -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, cast
|
11
|
+
from typing import TYPE_CHECKING, Dict, cast
|
12
12
|
from weakref import finalize
|
13
13
|
|
14
14
|
from prompt_toolkit.buffer import ValidationState
|
@@ -30,7 +30,7 @@ from prompt_toolkit.key_binding.key_bindings import (
|
|
30
30
|
KeyBindings,
|
31
31
|
merge_key_bindings,
|
32
32
|
)
|
33
|
-
from prompt_toolkit.layout.containers import ConditionalContainer, Float, Window
|
33
|
+
from prompt_toolkit.layout.containers import ConditionalContainer, Float, VSplit, Window
|
34
34
|
from prompt_toolkit.layout.controls import (
|
35
35
|
BufferControl,
|
36
36
|
FormattedTextControl,
|
@@ -46,11 +46,11 @@ from prompt_toolkit.utils import Event
|
|
46
46
|
from prompt_toolkit.validation import Validator
|
47
47
|
from prompt_toolkit.widgets.base import Box, TextArea
|
48
48
|
|
49
|
-
from euporie.core.border import
|
49
|
+
from euporie.core.border import InsetGrid
|
50
50
|
from euporie.core.current import get_app
|
51
51
|
from euporie.core.data_structures import DiBool
|
52
52
|
from euporie.core.formatted_text.utils import FormattedTextAlign, align
|
53
|
-
from euporie.core.margins import ScrollbarMargin
|
53
|
+
from euporie.core.margins import MarginContainer, ScrollbarMargin
|
54
54
|
from euporie.core.widgets.decor import Border, Shadow
|
55
55
|
from euporie.core.widgets.layout import ConditionalSplit
|
56
56
|
|
@@ -95,7 +95,7 @@ class Swatch:
|
|
95
95
|
width: int = 2,
|
96
96
|
height: int = 1,
|
97
97
|
style: str = "class:swatch",
|
98
|
-
border: GridStyle =
|
98
|
+
border: GridStyle = InsetGrid,
|
99
99
|
show_borders: DiBool | None = None,
|
100
100
|
) -> None:
|
101
101
|
"""Create a new instance of the color swatch.
|
@@ -143,7 +143,7 @@ class Button:
|
|
143
143
|
disabled: FilterOrBool = False,
|
144
144
|
width: int | None = None,
|
145
145
|
style: str | Callable[[], str] = "class:input",
|
146
|
-
border: GridStyle | None =
|
146
|
+
border: GridStyle | None = InsetGrid,
|
147
147
|
show_borders: DiBool | None = None,
|
148
148
|
selected: bool = False,
|
149
149
|
key_bindings: KeyBindingsBase | None = None,
|
@@ -259,7 +259,11 @@ class Button:
|
|
259
259
|
self.on_mouse_down.fire()
|
260
260
|
|
261
261
|
if not self.has_focus():
|
262
|
-
get_app()
|
262
|
+
app = get_app()
|
263
|
+
app.layout.focus(self)
|
264
|
+
# Invalidate the app - we don't do it by returning None so the
|
265
|
+
# event can bubble
|
266
|
+
app.invalidate()
|
263
267
|
# We want this event to bubble if unfocused
|
264
268
|
return NotImplemented
|
265
269
|
else:
|
@@ -305,7 +309,7 @@ class Button:
|
|
305
309
|
|
306
310
|
|
307
311
|
class ToggleableWidget(metaclass=ABCMeta):
|
308
|
-
"""
|
312
|
+
"""Base class for toggleable widgets."""
|
309
313
|
|
310
314
|
container: AnyContainer
|
311
315
|
on_click: Event
|
@@ -325,7 +329,9 @@ class ToggleableWidget(metaclass=ABCMeta):
|
|
325
329
|
elif mouse_event.event_type == MouseEventType.MOUSE_DOWN:
|
326
330
|
layout = get_app().layout
|
327
331
|
if not layout.has_focus(self):
|
328
|
-
get_app()
|
332
|
+
app = get_app()
|
333
|
+
app.layout.focus(self)
|
334
|
+
app.invalidate()
|
329
335
|
return NotImplemented
|
330
336
|
else:
|
331
337
|
return None
|
@@ -363,7 +369,7 @@ class ToggleButton(ToggleableWidget):
|
|
363
369
|
on_click: Callable[[ToggleButton], None] | None = None,
|
364
370
|
width: int | None = None,
|
365
371
|
style: str | Callable[[], str] = "class:input",
|
366
|
-
border: GridStyle | None =
|
372
|
+
border: GridStyle | None = InsetGrid,
|
367
373
|
show_borders: DiBool | None = None,
|
368
374
|
selected: bool = False,
|
369
375
|
disabled: FilterOrBool = False,
|
@@ -533,7 +539,7 @@ class Text:
|
|
533
539
|
style: str = "class:input",
|
534
540
|
height: int = 1,
|
535
541
|
min_height: int = 1,
|
536
|
-
multiline:
|
542
|
+
multiline: FilterOrBool = False,
|
537
543
|
expand: FilterOrBool = True,
|
538
544
|
width: int | None = None,
|
539
545
|
completer: Completer | None = None,
|
@@ -637,18 +643,23 @@ class Text:
|
|
637
643
|
)
|
638
644
|
self.text_area.window.content = self.text_area.control
|
639
645
|
|
640
|
-
if multiline:
|
641
|
-
self.text_area.window.right_margins = [
|
642
|
-
*self.text_area.window.right_margins,
|
643
|
-
ScrollbarMargin(),
|
644
|
-
]
|
645
646
|
if on_text_changed:
|
646
647
|
self.text_area.buffer.on_text_changed += on_text_changed
|
647
648
|
if validation:
|
648
649
|
self.text_area.buffer.validate_while_typing = Always()
|
649
650
|
self.container = Border(
|
650
|
-
|
651
|
-
|
651
|
+
VSplit(
|
652
|
+
[
|
653
|
+
self.text_area,
|
654
|
+
ConditionalContainer(
|
655
|
+
MarginContainer(
|
656
|
+
ScrollbarMargin(), target=self.text_area.window
|
657
|
+
),
|
658
|
+
filter=to_filter(multiline),
|
659
|
+
),
|
660
|
+
]
|
661
|
+
),
|
662
|
+
border=InsetGrid,
|
652
663
|
style=self.border_style,
|
653
664
|
show_borders=show_borders,
|
654
665
|
)
|
@@ -905,7 +916,7 @@ class Progress:
|
|
905
916
|
height=lambda: None if self.vertical() else 1,
|
906
917
|
width=lambda: 1 if self.vertical() else None,
|
907
918
|
),
|
908
|
-
border=
|
919
|
+
border=InsetGrid,
|
909
920
|
style=self.add_style("class:progress,border"),
|
910
921
|
),
|
911
922
|
# width=lambda: 1 if self.vertical() else None,
|
@@ -937,8 +948,8 @@ class Progress:
|
|
937
948
|
return self.container
|
938
949
|
|
939
950
|
|
940
|
-
class SizedMask(
|
941
|
-
"""
|
951
|
+
class SizedMask(Dict[int, bool]):
|
952
|
+
"""Mask with restricted number of True items."""
|
942
953
|
|
943
954
|
def __init__(self, size: int | None = None) -> None:
|
944
955
|
"""Initialize a new sized default dict."""
|
@@ -974,7 +985,7 @@ class SizedMask(dict[int, bool]):
|
|
974
985
|
|
975
986
|
|
976
987
|
class SelectableWidget(metaclass=ABCMeta):
|
977
|
-
"""
|
988
|
+
"""Base class for widgets where one or more items can be selected."""
|
978
989
|
|
979
990
|
def __init__(
|
980
991
|
self,
|
@@ -1133,6 +1144,22 @@ class SelectableWidget(metaclass=ABCMeta):
|
|
1133
1144
|
for i in range(len(self.options)):
|
1134
1145
|
self._selected[i] = i in values
|
1135
1146
|
|
1147
|
+
@property
|
1148
|
+
def value(self) -> Any:
|
1149
|
+
"""Return the selected value."""
|
1150
|
+
if self.options:
|
1151
|
+
return self.options[self.index or 0]
|
1152
|
+
else:
|
1153
|
+
return None
|
1154
|
+
|
1155
|
+
@property
|
1156
|
+
def values(self) -> list[Any]:
|
1157
|
+
"""Return a list of the selected values."""
|
1158
|
+
if self.options:
|
1159
|
+
return [self.options[i] for i in self.indices]
|
1160
|
+
else:
|
1161
|
+
return [None for i in self.indices]
|
1162
|
+
|
1136
1163
|
def mouse_handler(self, i: int, mouse_event: MouseEvent) -> NotImplementedOrNone:
|
1137
1164
|
"""Handle mouse events."""
|
1138
1165
|
if self.disabled():
|
@@ -1142,7 +1169,9 @@ class SelectableWidget(metaclass=ABCMeta):
|
|
1142
1169
|
return None
|
1143
1170
|
elif mouse_event.event_type == MouseEventType.MOUSE_DOWN:
|
1144
1171
|
if not self.has_focus():
|
1172
|
+
app = get_app()
|
1145
1173
|
get_app().layout.focus(self)
|
1174
|
+
app.invalidate()
|
1146
1175
|
# We want this event to bubble if unfocused
|
1147
1176
|
return NotImplemented
|
1148
1177
|
else:
|
@@ -1201,7 +1230,7 @@ class Select(SelectableWidget):
|
|
1201
1230
|
style: str | Callable[[], str] = "class:input,select",
|
1202
1231
|
rows: int | None = 3,
|
1203
1232
|
prefix: tuple[str, str] = ("", ""),
|
1204
|
-
border: GridStyle | None =
|
1233
|
+
border: GridStyle | None = InsetGrid,
|
1205
1234
|
show_borders: DiBool | None = None,
|
1206
1235
|
disabled: FilterOrBool = False,
|
1207
1236
|
dont_extend_width: FilterOrBool = True,
|
@@ -1252,7 +1281,12 @@ class Select(SelectableWidget):
|
|
1252
1281
|
def text_fragments(self) -> StyleAndTextTuples:
|
1253
1282
|
"""Create a list of formatted text fragments to display."""
|
1254
1283
|
ft: StyleAndTextTuples = []
|
1255
|
-
|
1284
|
+
if self.labels:
|
1285
|
+
max_width = max(
|
1286
|
+
fragment_list_width(to_formatted_text(x)) for x in self.labels
|
1287
|
+
)
|
1288
|
+
else:
|
1289
|
+
max_width = 1
|
1256
1290
|
for i, label in enumerate(self.labels):
|
1257
1291
|
label = to_formatted_text(label)
|
1258
1292
|
label = align(label, FormattedTextAlign.LEFT, width=max_width)
|
@@ -1277,26 +1311,31 @@ class Select(SelectableWidget):
|
|
1277
1311
|
(f"{fragment_style} {style}", text, handler)
|
1278
1312
|
for fragment_style, text, *_ in ft_option
|
1279
1313
|
]
|
1280
|
-
ft
|
1314
|
+
if ft:
|
1315
|
+
ft.pop()
|
1281
1316
|
return ft
|
1282
1317
|
|
1283
1318
|
def load_container(self) -> AnyContainer:
|
1284
1319
|
"""Load the widget's container."""
|
1285
1320
|
return Box(
|
1286
1321
|
Border(
|
1287
|
-
|
1288
|
-
|
1289
|
-
|
1290
|
-
|
1291
|
-
|
1292
|
-
|
1293
|
-
|
1294
|
-
|
1295
|
-
|
1296
|
-
|
1297
|
-
|
1298
|
-
|
1299
|
-
|
1322
|
+
VSplit(
|
1323
|
+
[
|
1324
|
+
window := Window(
|
1325
|
+
FormattedTextControl(
|
1326
|
+
self.text_fragments,
|
1327
|
+
focusable=True,
|
1328
|
+
show_cursor=False,
|
1329
|
+
key_bindings=self.key_bindings(),
|
1330
|
+
),
|
1331
|
+
height=lambda: self.rows,
|
1332
|
+
dont_extend_width=self.dont_extend_width,
|
1333
|
+
dont_extend_height=self.dont_extend_height,
|
1334
|
+
style=f"class:face"
|
1335
|
+
f"{' class:disabled' if self.disabled() else ''}",
|
1336
|
+
),
|
1337
|
+
MarginContainer(ScrollbarMargin(), target=window),
|
1338
|
+
]
|
1300
1339
|
),
|
1301
1340
|
border=self.border,
|
1302
1341
|
show_borders=self.show_borders,
|
@@ -1510,7 +1549,7 @@ class ToggleButtons(SelectableWidget):
|
|
1510
1549
|
max_count: int | None = None,
|
1511
1550
|
on_change: Callable[[SelectableWidget], None] | None = None,
|
1512
1551
|
style: str | Callable[[], str] = "class:input",
|
1513
|
-
border: GridStyle | None =
|
1552
|
+
border: GridStyle | None = InsetGrid,
|
1514
1553
|
disabled: FilterOrBool = False,
|
1515
1554
|
vertical: FilterOrBool = False,
|
1516
1555
|
) -> None:
|
@@ -2055,7 +2094,7 @@ class Slider(SelectableWidget):
|
|
2055
2094
|
max_count: int | None = None,
|
2056
2095
|
on_change: Callable[[SelectableWidget], None] | None = None,
|
2057
2096
|
style: str | Callable[[], str] = "class:input",
|
2058
|
-
border: GridStyle =
|
2097
|
+
border: GridStyle = InsetGrid,
|
2059
2098
|
show_borders: DiBool | None = None,
|
2060
2099
|
vertical: FilterOrBool = False,
|
2061
2100
|
show_arrows: FilterOrBool = True,
|
euporie/core/widgets/inputs.py
CHANGED
@@ -79,7 +79,6 @@ class KernelInput(TextArea):
|
|
79
79
|
tempfile_suffix: str | Callable[[], str] = "",
|
80
80
|
key_bindings: KeyBindingsBase | None = None,
|
81
81
|
enable_history_search: FilterOrBool | None = False,
|
82
|
-
focusable: FilterOrBool = True,
|
83
82
|
wrap_lines: FilterOrBool = False,
|
84
83
|
complete_while_typing: FilterOrBool = True,
|
85
84
|
autosuggest_while_typing: FilterOrBool = True,
|
@@ -95,6 +94,7 @@ class KernelInput(TextArea):
|
|
95
94
|
kwargs.setdefault("history", kernel_tab.history)
|
96
95
|
kwargs.setdefault("search_field", app.search_bar)
|
97
96
|
kwargs.setdefault("focus_on_click", True)
|
97
|
+
kwargs.setdefault("preview_search", True)
|
98
98
|
kwargs.setdefault(
|
99
99
|
"input_processors",
|
100
100
|
[
|
@@ -168,18 +168,28 @@ class KernelInput(TextArea):
|
|
168
168
|
)
|
169
169
|
|
170
170
|
# Add configurable line numbers
|
171
|
-
self.window.left_margins =
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
171
|
+
self.window.left_margins = (
|
172
|
+
left_margins
|
173
|
+
if left_margins is not None
|
174
|
+
else [
|
175
|
+
ConditionalMargin(
|
176
|
+
NumberedDiffMargin(),
|
177
|
+
Condition(lambda: self.kernel_tab.app.config.line_numbers),
|
178
|
+
)
|
179
|
+
]
|
180
|
+
)
|
181
|
+
|
182
|
+
self.window.right_margins = (
|
183
|
+
right_margins
|
184
|
+
if right_margins is not None
|
185
|
+
else [
|
186
|
+
OverflowMargin(),
|
187
|
+
ConditionalMargin(
|
188
|
+
ScrollbarMargin(display_arrows=False),
|
189
|
+
filter=scrollable(self.window),
|
190
|
+
),
|
191
|
+
]
|
192
|
+
)
|
183
193
|
|
184
194
|
self.window.cursorline = self.has_focus
|
185
195
|
|
euporie/core/widgets/layout.py
CHANGED
@@ -30,7 +30,7 @@ from prompt_toolkit.mouse_events import MouseButton, MouseEventType
|
|
30
30
|
from prompt_toolkit.utils import Event
|
31
31
|
from prompt_toolkit.widgets import Box
|
32
32
|
|
33
|
-
from euporie.core.border import
|
33
|
+
from euporie.core.border import OutsetGrid
|
34
34
|
from euporie.core.data_structures import DiBool
|
35
35
|
from euporie.core.widgets.decor import Border
|
36
36
|
|
@@ -340,7 +340,7 @@ class TabBarControl(UIControl):
|
|
340
340
|
|
341
341
|
|
342
342
|
class StackedSplit(metaclass=ABCMeta):
|
343
|
-
"""
|
343
|
+
"""Base class for containers with selectable children."""
|
344
344
|
|
345
345
|
def __init__(
|
346
346
|
self,
|
@@ -454,7 +454,7 @@ class TabbedSplit(StackedSplit):
|
|
454
454
|
on_change: Callable[[StackedSplit], None] | None = None,
|
455
455
|
width: AnyDimension = None,
|
456
456
|
height: AnyDimension = None,
|
457
|
-
border: GridStyle =
|
457
|
+
border: GridStyle = OutsetGrid,
|
458
458
|
show_borders: DiBool | None = None,
|
459
459
|
) -> None:
|
460
460
|
"""Initialize a new tabbed container."""
|
@@ -493,6 +493,7 @@ class TabbedSplit(StackedSplit):
|
|
493
493
|
padding=0,
|
494
494
|
padding_top=1,
|
495
495
|
padding_bottom=1,
|
496
|
+
style="class:tabbed-split,page",
|
496
497
|
),
|
497
498
|
border=self.border,
|
498
499
|
show_borders=self.show_borders,
|