euporie 2.6.1__py3-none-any.whl → 2.7.0__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/tabs/console.py +51 -43
- euporie/core/__init__.py +5 -2
- euporie/core/app.py +74 -57
- euporie/core/comm/ipywidgets.py +7 -3
- euporie/core/config.py +51 -27
- euporie/core/convert/__init__.py +2 -0
- euporie/core/convert/datum.py +82 -45
- euporie/core/convert/formats/ansi.py +1 -2
- euporie/core/convert/formats/common.py +7 -11
- euporie/core/convert/formats/ft.py +10 -7
- euporie/core/convert/formats/png.py +7 -6
- euporie/core/convert/formats/sixel.py +1 -1
- euporie/core/convert/formats/svg.py +28 -0
- euporie/core/convert/mime.py +4 -7
- euporie/core/data_structures.py +24 -22
- euporie/core/filters.py +16 -2
- euporie/core/format.py +30 -4
- euporie/core/ft/ansi.py +2 -1
- euporie/core/ft/html.py +155 -42
- euporie/core/{widgets/graphics.py → graphics.py} +225 -227
- euporie/core/io.py +8 -0
- euporie/core/key_binding/bindings/__init__.py +8 -2
- euporie/core/key_binding/bindings/basic.py +9 -14
- euporie/core/key_binding/bindings/micro.py +0 -12
- euporie/core/key_binding/bindings/mouse.py +107 -80
- euporie/core/key_binding/bindings/page_navigation.py +129 -0
- euporie/core/key_binding/key_processor.py +9 -1
- euporie/core/layout/__init__.py +1 -0
- euporie/core/layout/containers.py +1011 -0
- euporie/core/layout/decor.py +381 -0
- euporie/core/layout/print.py +130 -0
- euporie/core/layout/screen.py +75 -0
- euporie/core/{widgets/page.py → layout/scroll.py} +166 -111
- euporie/core/log.py +1 -1
- euporie/core/margins.py +11 -5
- euporie/core/path.py +43 -176
- euporie/core/renderer.py +31 -8
- euporie/core/style.py +2 -0
- euporie/core/tabs/base.py +2 -1
- euporie/core/terminal.py +19 -21
- euporie/core/widgets/cell.py +2 -4
- euporie/core/widgets/cell_outputs.py +2 -2
- euporie/core/widgets/decor.py +3 -359
- euporie/core/widgets/dialog.py +5 -5
- euporie/core/widgets/display.py +32 -12
- euporie/core/widgets/file_browser.py +3 -4
- euporie/core/widgets/forms.py +36 -14
- euporie/core/widgets/inputs.py +171 -99
- euporie/core/widgets/layout.py +80 -5
- euporie/core/widgets/menu.py +1 -3
- euporie/core/widgets/pager.py +3 -3
- euporie/core/widgets/palette.py +3 -2
- euporie/core/widgets/status_bar.py +2 -6
- euporie/core/widgets/tree.py +3 -6
- euporie/notebook/app.py +8 -8
- euporie/notebook/tabs/notebook.py +2 -2
- euporie/notebook/widgets/side_bar.py +1 -1
- euporie/preview/tabs/notebook.py +2 -2
- euporie/web/tabs/web.py +6 -1
- euporie/web/widgets/webview.py +52 -32
- {euporie-2.6.1.dist-info → euporie-2.7.0.dist-info}/METADATA +9 -11
- {euporie-2.6.1.dist-info → euporie-2.7.0.dist-info}/RECORD +67 -60
- {euporie-2.6.1.dist-info → euporie-2.7.0.dist-info}/WHEEL +1 -1
- {euporie-2.6.1.data → euporie-2.7.0.data}/data/share/applications/euporie-console.desktop +0 -0
- {euporie-2.6.1.data → euporie-2.7.0.data}/data/share/applications/euporie-notebook.desktop +0 -0
- {euporie-2.6.1.dist-info → euporie-2.7.0.dist-info}/entry_points.txt +0 -0
- {euporie-2.6.1.dist-info → euporie-2.7.0.dist-info}/licenses/LICENSE +0 -0
euporie/console/tabs/console.py
CHANGED
@@ -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
|
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
|
-
|
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
|
-
|
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
|
205
|
-
|
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
|
-
|
212
|
-
and len(self.json["cells"]) >
|
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
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
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=
|
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
|
-
|
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
|
-
|
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
|
euporie/core/__init__.py
CHANGED
@@ -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.
|
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"©
|
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
|
euporie/core/app.py
CHANGED
@@ -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,
|
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
|
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
|
-
#
|
380
|
-
|
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(
|
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
|
-
|
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_=
|
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="
|
961
|
-
flags=["--
|
962
|
-
type_=
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
|
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
|
-
|
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="
|
1024
|
-
flags=["--
|
1033
|
+
name="multiplexer_passthrough",
|
1034
|
+
flags=["--multiplexer-passthrough"],
|
1025
1035
|
type_=bool,
|
1026
|
-
help_="
|
1036
|
+
help_="Use passthrough from within terminal multiplexers",
|
1027
1037
|
default=False,
|
1028
|
-
hidden=~
|
1038
|
+
hidden=~in_mplex,
|
1029
1039
|
description="""
|
1030
|
-
If set
|
1031
|
-
|
1032
|
-
|
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
|
-
|
1035
|
-
|
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
|
|
euporie/core/comm/ipywidgets.py
CHANGED
@@ -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
|
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
|
euporie/core/config.py
CHANGED
@@ -45,7 +45,7 @@ _SCHEMA_TYPES: dict[type | Callable, str] = {
|
|
45
45
|
bool: "boolean",
|
46
46
|
str: "string",
|
47
47
|
int: "integer",
|
48
|
-
float: "
|
48
|
+
float: "number",
|
49
49
|
UPath: "string",
|
50
50
|
}
|
51
51
|
|
@@ -212,17 +212,40 @@ class Setting:
|
|
212
212
|
filter=self.cmd_filter,
|
213
213
|
)(self.toggle)
|
214
214
|
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
215
|
+
schema = self.schema
|
216
|
+
if schema.get("type") == "array":
|
217
|
+
for choice in self.choices or schema.get("items", {}).get("enum") or []:
|
218
|
+
add_cmd(
|
219
|
+
name=f"toggle-{name}-{choice}",
|
220
|
+
hidden=self.hidden,
|
221
|
+
toggled=Condition(partial(lambda x: x in self.value, choice)),
|
222
|
+
title=f"Toggle {choice} into {self.title}",
|
223
|
+
menu_title=str(choice).replace("_", " ").capitalize(),
|
224
|
+
description=f'Add or remove "{choice}" to or from the list of "{self.name}"',
|
225
|
+
filter=self.cmd_filter,
|
226
|
+
)(
|
227
|
+
partial(
|
228
|
+
lambda choice: (
|
229
|
+
self.value.remove
|
230
|
+
if choice in self.value
|
231
|
+
else self.value.append
|
232
|
+
)(choice),
|
233
|
+
choice,
|
234
|
+
)
|
235
|
+
)
|
236
|
+
|
237
|
+
else:
|
238
|
+
for choice in self.choices or schema.get("enum", []) or []:
|
239
|
+
add_cmd(
|
240
|
+
name=f"set-{name}-{choice}",
|
241
|
+
hidden=self.hidden,
|
242
|
+
toggled=Condition(partial(lambda x: self.value == x, choice)),
|
243
|
+
title=f"Set {self.title} to {choice}",
|
244
|
+
menu_title=str(choice).replace("_", " ").capitalize(),
|
245
|
+
description=f'Set the value of the "{self.name}" '
|
246
|
+
f'configuration option to "{choice}"',
|
247
|
+
filter=self.cmd_filter,
|
248
|
+
)(partial(setattr, self, "value", choice))
|
226
249
|
|
227
250
|
def toggle(self) -> None:
|
228
251
|
"""Toggle the setting's value."""
|
@@ -256,12 +279,17 @@ class Setting:
|
|
256
279
|
@property
|
257
280
|
def schema(self) -> dict[str, Any]:
|
258
281
|
"""Return a json schema property for the config item."""
|
259
|
-
|
282
|
+
schema = {
|
260
283
|
"description": self.help,
|
261
|
-
**({"enum": self.choices} if self.choices is not None else {}),
|
262
284
|
**({"default": self.default} if self.default is not None else {}),
|
263
285
|
**self._schema,
|
264
286
|
}
|
287
|
+
if self.choices:
|
288
|
+
if self.nargs == "*" or "items" in schema:
|
289
|
+
schema["items"]["enum"] = self.choices
|
290
|
+
else:
|
291
|
+
schema["enum"] = self.choices
|
292
|
+
return schema
|
265
293
|
|
266
294
|
@property
|
267
295
|
def menu(self) -> MenuItem:
|
@@ -378,19 +406,14 @@ class Config:
|
|
378
406
|
|
379
407
|
# Save a list of unknown configuration options so we can warn about them once
|
380
408
|
# the logs are configured
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
"""Warn about unrecognised configuration items."""
|
390
|
-
for map_name, option_name in self.unrecognised:
|
391
|
-
log.warning(
|
392
|
-
"Configuration option '%s' not recognised in %s", option_name, map_name
|
393
|
-
)
|
409
|
+
for map_name, map_values in config_maps.items():
|
410
|
+
for option_name in map_values.keys() - Config.settings.keys():
|
411
|
+
if not isinstance(set_values[option_name], dict):
|
412
|
+
log.warning(
|
413
|
+
"Configuration option '%s' not recognised in %s",
|
414
|
+
option_name,
|
415
|
+
map_name,
|
416
|
+
)
|
394
417
|
|
395
418
|
@property
|
396
419
|
def schema(self) -> dict[str, Any]:
|
@@ -432,6 +455,7 @@ class Config:
|
|
432
455
|
fastjsonschema.validate(self.schema, json_data)
|
433
456
|
except fastjsonschema.JsonSchemaValueException as error:
|
434
457
|
log.warning("Error in command line parameter `%s`: %s", name, error)
|
458
|
+
log.warning("%s: %s", name, value)
|
435
459
|
else:
|
436
460
|
result[name] = value
|
437
461
|
return result
|