euporie 2.8.3__py3-none-any.whl → 2.8.4__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 +227 -104
- euporie/core/__init__.py +1 -1
- euporie/core/__main__.py +1 -1
- euporie/core/app.py +20 -18
- euporie/core/clipboard.py +1 -1
- euporie/core/comm/ipywidgets.py +5 -5
- euporie/core/commands.py +1 -1
- euporie/core/config.py +4 -4
- euporie/core/convert/datum.py +4 -1
- euporie/core/convert/registry.py +7 -2
- euporie/core/filters.py +3 -1
- euporie/core/ft/html.py +2 -4
- euporie/core/graphics.py +6 -6
- euporie/core/kernel.py +56 -32
- euporie/core/key_binding/bindings/__init__.py +2 -1
- euporie/core/key_binding/bindings/mouse.py +24 -22
- euporie/core/key_binding/bindings/vi.py +46 -0
- euporie/core/layout/cache.py +33 -23
- euporie/core/layout/containers.py +235 -73
- euporie/core/layout/decor.py +3 -3
- euporie/core/layout/print.py +14 -2
- euporie/core/layout/scroll.py +15 -21
- euporie/core/margins.py +59 -30
- euporie/core/style.py +7 -5
- euporie/core/tabs/base.py +32 -0
- euporie/core/tabs/notebook.py +6 -3
- euporie/core/terminal.py +12 -17
- euporie/core/utils.py +2 -4
- euporie/core/widgets/cell.py +64 -109
- euporie/core/widgets/dialog.py +25 -20
- euporie/core/widgets/file_browser.py +3 -3
- euporie/core/widgets/forms.py +8 -7
- euporie/core/widgets/layout.py +5 -5
- euporie/core/widgets/status.py +3 -3
- euporie/hub/app.py +7 -3
- euporie/notebook/app.py +59 -46
- euporie/notebook/tabs/log.py +1 -1
- euporie/notebook/tabs/notebook.py +5 -3
- euporie/preview/app.py +3 -0
- euporie/preview/tabs/notebook.py +9 -14
- euporie/web/tabs/web.py +0 -1
- {euporie-2.8.3.dist-info → euporie-2.8.4.dist-info}/METADATA +5 -5
- {euporie-2.8.3.dist-info → euporie-2.8.4.dist-info}/RECORD +48 -47
- {euporie-2.8.3.data → euporie-2.8.4.data}/data/share/applications/euporie-console.desktop +0 -0
- {euporie-2.8.3.data → euporie-2.8.4.data}/data/share/applications/euporie-notebook.desktop +0 -0
- {euporie-2.8.3.dist-info → euporie-2.8.4.dist-info}/WHEEL +0 -0
- {euporie-2.8.3.dist-info → euporie-2.8.4.dist-info}/entry_points.txt +0 -0
- {euporie-2.8.3.dist-info → euporie-2.8.4.dist-info}/licenses/LICENSE +0 -0
euporie/core/widgets/cell.py
CHANGED
@@ -49,7 +49,7 @@ if TYPE_CHECKING:
|
|
49
49
|
|
50
50
|
from prompt_toolkit.buffer import Buffer
|
51
51
|
from prompt_toolkit.completion.base import Completer
|
52
|
-
from prompt_toolkit.formatted_text.base import
|
52
|
+
from prompt_toolkit.formatted_text.base import StyleAndTextTuples
|
53
53
|
|
54
54
|
from euporie.core.format import Formatter
|
55
55
|
from euporie.core.inspection import Inspector
|
@@ -234,36 +234,11 @@ class Cell:
|
|
234
234
|
fill = partial(Window, style="class:border")
|
235
235
|
|
236
236
|
# Create textbox for standard input
|
237
|
-
def _send_input(buf: Buffer) -> bool:
|
238
|
-
return False
|
239
|
-
|
240
237
|
self.stdin_box = StdInput(weak_self.kernel_tab)
|
241
238
|
|
242
|
-
top_border = VSplit(
|
243
|
-
[
|
244
|
-
self.control,
|
245
|
-
ConditionalContainer(
|
246
|
-
content=fill(
|
247
|
-
char=border_char("TOP_MID"),
|
248
|
-
width=lambda: len(weak_self.prompt),
|
249
|
-
height=1,
|
250
|
-
),
|
251
|
-
filter=show_prompt,
|
252
|
-
),
|
253
|
-
ConditionalContainer(
|
254
|
-
content=fill(width=1, height=1, char=border_char("TOP_SPLIT")),
|
255
|
-
filter=show_prompt,
|
256
|
-
),
|
257
|
-
fill(char=border_char("TOP_MID"), height=1),
|
258
|
-
fill(width=1, height=1, char=border_char("TOP_RIGHT")),
|
259
|
-
],
|
260
|
-
height=1,
|
261
|
-
)
|
262
|
-
|
263
239
|
input_row = ConditionalContainer(
|
264
240
|
VSplit(
|
265
241
|
[
|
266
|
-
fill(width=1, char=border_char("MID_LEFT")),
|
267
242
|
ConditionalContainer(
|
268
243
|
content=Window(
|
269
244
|
FormattedTextControl(
|
@@ -276,63 +251,44 @@ class Cell:
|
|
276
251
|
("", "\n ", lambda e: NotImplemented),
|
277
252
|
]
|
278
253
|
),
|
279
|
-
width=lambda: len(weak_self.prompt),
|
254
|
+
width=lambda: len(weak_self.prompt) + 1,
|
280
255
|
height=Dimension(preferred=1),
|
281
256
|
style="class:input,prompt",
|
282
257
|
),
|
283
258
|
filter=show_prompt,
|
284
259
|
),
|
285
|
-
ConditionalContainer(
|
286
|
-
fill(width=1, char=border_char("MID_SPLIT")),
|
287
|
-
filter=show_prompt,
|
288
|
-
),
|
289
260
|
ConditionalContainer(self.input_box, filter=~source_hidden),
|
290
261
|
ConditionalContainer(
|
291
262
|
Window(
|
292
263
|
FormattedTextControl(
|
293
|
-
|
294
|
-
|
295
|
-
|
264
|
+
cast(
|
265
|
+
"StyleAndTextTuples",
|
266
|
+
[
|
267
|
+
(
|
268
|
+
"class:cell,show,inputs,border",
|
269
|
+
"▏",
|
270
|
+
on_click(self.show_input),
|
271
|
+
),
|
296
272
|
(
|
297
273
|
"class:cell,show,inputs",
|
298
|
-
"
|
274
|
+
"…",
|
299
275
|
on_click(self.show_input),
|
300
276
|
),
|
301
|
-
|
302
|
-
|
277
|
+
(
|
278
|
+
"class:cell,show,inputs,border",
|
279
|
+
"▕",
|
280
|
+
on_click(self.show_input),
|
281
|
+
),
|
282
|
+
],
|
283
|
+
)
|
303
284
|
)
|
304
285
|
),
|
305
286
|
filter=source_hidden,
|
306
287
|
),
|
307
|
-
fill(width=1, char=border_char("MID_RIGHT")),
|
308
288
|
],
|
309
289
|
),
|
310
290
|
filter=show_input,
|
311
291
|
)
|
312
|
-
middle_line = ConditionalContainer(
|
313
|
-
content=VSplit(
|
314
|
-
[
|
315
|
-
fill(width=1, height=1, char=border_char("SPLIT_LEFT")),
|
316
|
-
ConditionalContainer(
|
317
|
-
content=fill(
|
318
|
-
char=border_char("SPLIT_MID"),
|
319
|
-
width=lambda: len(weak_self.prompt),
|
320
|
-
),
|
321
|
-
filter=show_prompt,
|
322
|
-
),
|
323
|
-
ConditionalContainer(
|
324
|
-
content=fill(
|
325
|
-
width=1, height=1, char=border_char("SPLIT_SPLIT")
|
326
|
-
),
|
327
|
-
filter=show_prompt,
|
328
|
-
),
|
329
|
-
fill(char=border_char("SPLIT_MID")),
|
330
|
-
fill(width=1, height=1, char=border_char("SPLIT_RIGHT")),
|
331
|
-
],
|
332
|
-
height=1,
|
333
|
-
),
|
334
|
-
filter=(show_input & show_output) | self.stdin_box.visible,
|
335
|
-
)
|
336
292
|
|
337
293
|
outputs_hidden = Condition(
|
338
294
|
lambda: weak_self.json["metadata"]
|
@@ -344,7 +300,6 @@ class Cell:
|
|
344
300
|
output_row = ConditionalContainer(
|
345
301
|
VSplit(
|
346
302
|
[
|
347
|
-
fill(width=1, char=border_char("MID_LEFT")),
|
348
303
|
ConditionalContainer(
|
349
304
|
content=Window(
|
350
305
|
FormattedTextControl(
|
@@ -357,20 +312,12 @@ class Cell:
|
|
357
312
|
("", "\n ", lambda e: NotImplemented),
|
358
313
|
],
|
359
314
|
),
|
360
|
-
width=lambda: len(weak_self.prompt),
|
315
|
+
width=lambda: len(weak_self.prompt) + 1,
|
361
316
|
height=Dimension(preferred=1),
|
362
317
|
style="class:output,prompt",
|
363
318
|
),
|
364
319
|
filter=show_prompt,
|
365
320
|
),
|
366
|
-
ConditionalContainer(
|
367
|
-
content=fill(width=1, char=border_char("MID_SPLIT")),
|
368
|
-
filter=show_prompt,
|
369
|
-
),
|
370
|
-
ConditionalContainer(
|
371
|
-
fill(width=1, char=border_char("MID_MID")),
|
372
|
-
filter=~show_prompt,
|
373
|
-
),
|
374
321
|
HSplit(
|
375
322
|
[
|
376
323
|
ConditionalContainer(
|
@@ -380,51 +327,38 @@ class Cell:
|
|
380
327
|
ConditionalContainer(
|
381
328
|
Window(
|
382
329
|
FormattedTextControl(
|
383
|
-
|
384
|
-
|
385
|
-
|
330
|
+
cast(
|
331
|
+
"StyleAndTextTuples",
|
332
|
+
[
|
333
|
+
(
|
334
|
+
"class:cell,show,outputs,border",
|
335
|
+
"▏",
|
336
|
+
on_click(self.show_output),
|
337
|
+
),
|
386
338
|
(
|
387
339
|
"class:cell,show,outputs",
|
388
|
-
"
|
340
|
+
"…",
|
389
341
|
on_click(self.show_output),
|
390
342
|
),
|
391
|
-
|
392
|
-
|
343
|
+
(
|
344
|
+
"class:cell,show,outputs,border",
|
345
|
+
"▕",
|
346
|
+
on_click(self.show_output),
|
347
|
+
),
|
348
|
+
],
|
349
|
+
)
|
393
350
|
)
|
394
351
|
),
|
395
352
|
filter=outputs_hidden,
|
396
353
|
),
|
397
354
|
self.stdin_box,
|
398
|
-
]
|
399
|
-
),
|
400
|
-
ConditionalContainer(
|
401
|
-
fill(width=1, char=border_char("MID_MID")),
|
402
|
-
filter=~show_prompt,
|
355
|
+
],
|
403
356
|
),
|
404
|
-
fill(width=1, char=border_char("MID_RIGHT")),
|
405
357
|
],
|
358
|
+
width=Dimension(min=1),
|
406
359
|
),
|
407
360
|
filter=show_output | self.stdin_box.visible,
|
408
361
|
)
|
409
|
-
bottom_border = VSplit(
|
410
|
-
[
|
411
|
-
fill(width=1, height=1, char=border_char("BOTTOM_LEFT")),
|
412
|
-
ConditionalContainer(
|
413
|
-
content=fill(
|
414
|
-
char=border_char("BOTTOM_MID"),
|
415
|
-
width=lambda: len(weak_self.prompt),
|
416
|
-
),
|
417
|
-
filter=show_prompt,
|
418
|
-
),
|
419
|
-
ConditionalContainer(
|
420
|
-
content=fill(width=1, height=1, char=border_char("BOTTOM_SPLIT")),
|
421
|
-
filter=show_prompt,
|
422
|
-
),
|
423
|
-
fill(char=border_char("BOTTOM_MID")),
|
424
|
-
fill(width=1, height=1, char=border_char("BOTTOM_RIGHT")),
|
425
|
-
],
|
426
|
-
height=1,
|
427
|
-
)
|
428
362
|
|
429
363
|
def _style() -> str:
|
430
364
|
"""Calculate the cell's style given its state."""
|
@@ -438,11 +372,32 @@ class Cell:
|
|
438
372
|
|
439
373
|
self.container = HSplit(
|
440
374
|
[
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
375
|
+
VSplit(
|
376
|
+
[
|
377
|
+
self.control,
|
378
|
+
fill(char=border_char("TOP_MID"), height=1),
|
379
|
+
fill(width=1, height=1, char=border_char("TOP_RIGHT")),
|
380
|
+
],
|
381
|
+
height=1,
|
382
|
+
),
|
383
|
+
VSplit(
|
384
|
+
[
|
385
|
+
fill(width=1, char=border_char("MID_LEFT")),
|
386
|
+
HSplit(
|
387
|
+
[input_row, output_row],
|
388
|
+
padding=lambda: 1 if show_input() and show_output() else 0,
|
389
|
+
),
|
390
|
+
fill(width=1, char=border_char("MID_RIGHT")),
|
391
|
+
]
|
392
|
+
),
|
393
|
+
VSplit(
|
394
|
+
[
|
395
|
+
fill(width=1, height=1, char=border_char("BOTTOM_LEFT")),
|
396
|
+
fill(char=border_char("BOTTOM_MID")),
|
397
|
+
fill(width=1, height=1, char=border_char("BOTTOM_RIGHT")),
|
398
|
+
],
|
399
|
+
height=1,
|
400
|
+
),
|
446
401
|
],
|
447
402
|
style=_style,
|
448
403
|
)
|
@@ -795,7 +750,7 @@ class Cell:
|
|
795
750
|
"""Set the execution count of the cell."""
|
796
751
|
self.json["execution_count"] = n
|
797
752
|
|
798
|
-
def add_output(self, output_json: dict[str, Any]) -> None:
|
753
|
+
def add_output(self, output_json: dict[str, Any], own: bool) -> None:
|
799
754
|
"""Add a new output to the cell."""
|
800
755
|
# Clear the output if we were previously asked to
|
801
756
|
if self.clear_outputs_on_output:
|
euporie/core/widgets/dialog.py
CHANGED
@@ -26,6 +26,7 @@ from prompt_toolkit.layout.containers import (
|
|
26
26
|
ConditionalContainer,
|
27
27
|
DynamicContainer,
|
28
28
|
Float,
|
29
|
+
to_container,
|
29
30
|
)
|
30
31
|
from prompt_toolkit.layout.controls import FormattedTextControl, UIContent, UIControl
|
31
32
|
from prompt_toolkit.layout.dimension import Dimension
|
@@ -189,7 +190,7 @@ class Dialog(Float, metaclass=ABCMeta):
|
|
189
190
|
# Set default body & buttons
|
190
191
|
self.body: AnyContainer = Window()
|
191
192
|
self.buttons: dict[str, Callable | None] = {"OK": None}
|
192
|
-
self.
|
193
|
+
self._button_widgets: list[AnyContainer] = []
|
193
194
|
|
194
195
|
# Create key-bindings
|
195
196
|
kb = KeyBindings()
|
@@ -225,16 +226,12 @@ class Dialog(Float, metaclass=ABCMeta):
|
|
225
226
|
)
|
226
227
|
|
227
228
|
# The buttons.
|
229
|
+
self.button_split = VSplit(self.button_widgets, padding=1)
|
228
230
|
buttons_row = ConditionalContainer(
|
229
231
|
Box(
|
230
|
-
body=
|
231
|
-
lambda: VSplit(
|
232
|
-
self.button_widgets,
|
233
|
-
padding=1,
|
234
|
-
key_bindings=DynamicKeyBindings(lambda: self.buttons_kb),
|
235
|
-
)
|
236
|
-
),
|
232
|
+
body=self.button_split,
|
237
233
|
height=Dimension(min=1, max=3, preferred=3),
|
234
|
+
key_bindings=DynamicKeyBindings(lambda: self.buttons_kb),
|
238
235
|
),
|
239
236
|
filter=Condition(lambda: bool(self.buttons)),
|
240
237
|
)
|
@@ -263,6 +260,16 @@ class Dialog(Float, metaclass=ABCMeta):
|
|
263
260
|
# Set the body as the float's contents
|
264
261
|
super().__init__(content=self.container)
|
265
262
|
|
263
|
+
@property
|
264
|
+
def button_widgets(self) -> list[AnyContainer]:
|
265
|
+
"""A list of button widgets to show in the dialog's row of buttons."""
|
266
|
+
return self._button_widgets
|
267
|
+
|
268
|
+
@button_widgets.setter
|
269
|
+
def button_widgets(self, value: list[AnyContainer]) -> None:
|
270
|
+
self._button_widgets = list(value)
|
271
|
+
self.button_split.children = [to_container(c) for c in value]
|
272
|
+
|
266
273
|
def _button_handler(
|
267
274
|
self, button: str = "", event: KeyPressEvent | None = None
|
268
275
|
) -> None:
|
@@ -280,8 +287,7 @@ class Dialog(Float, metaclass=ABCMeta):
|
|
280
287
|
self.load(**params)
|
281
288
|
|
282
289
|
# Create button widgets & callbacks
|
283
|
-
|
284
|
-
|
290
|
+
new_button_widgets: list[AnyContainer] = []
|
285
291
|
if self.buttons:
|
286
292
|
width = max(map(len, self.buttons)) + 2
|
287
293
|
used_keys = set()
|
@@ -296,7 +302,7 @@ class Dialog(Float, metaclass=ABCMeta):
|
|
296
302
|
rest = text
|
297
303
|
# Add a button with a handler
|
298
304
|
handler = partial(self._button_handler, text)
|
299
|
-
|
305
|
+
new_button_widgets.append(
|
300
306
|
FocusedStyle(
|
301
307
|
Button(
|
302
308
|
[("underline", key), ("", rest)],
|
@@ -309,6 +315,7 @@ class Dialog(Float, metaclass=ABCMeta):
|
|
309
315
|
# Add a key-handler
|
310
316
|
if key:
|
311
317
|
self.buttons_kb.add(f"A-{key.lower()}", is_global=True)(handler)
|
318
|
+
self.button_widgets = new_button_widgets
|
312
319
|
|
313
320
|
# When a button is selected, handle left/right key bindings.
|
314
321
|
if len(self.button_widgets) > 1:
|
@@ -333,15 +340,13 @@ class Dialog(Float, metaclass=ABCMeta):
|
|
333
340
|
self._load(**params)
|
334
341
|
self.last_focused = self.app.layout.current_control
|
335
342
|
self._visible = True
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
self.app.layout.focus(self.container)
|
344
|
-
self.app.invalidate()
|
343
|
+
to_focus = self.to_focus or self.container
|
344
|
+
try:
|
345
|
+
self.app.layout.focus(to_focus)
|
346
|
+
except ValueError:
|
347
|
+
pass
|
348
|
+
finally:
|
349
|
+
self.app.invalidate()
|
345
350
|
|
346
351
|
def hide(self, event: KeyPressEvent | None = None) -> None:
|
347
352
|
"""Hide the dialog."""
|
@@ -377,9 +377,9 @@ class FileBrowserControl(UIControl):
|
|
377
377
|
self.dir = path or UPath(".")
|
378
378
|
self.hovered: int | None = None
|
379
379
|
self.selected: int | None = None
|
380
|
-
self._dir_cache: FastDictCache[
|
381
|
-
|
382
|
-
|
380
|
+
self._dir_cache: FastDictCache[tuple[Path], list[tuple[bool, Path]]] = (
|
381
|
+
FastDictCache(get_value=self.load_path, size=1)
|
382
|
+
)
|
383
383
|
self.on_select = Event(self, on_select)
|
384
384
|
self.on_chdir = Event(self, on_chdir)
|
385
385
|
self.on_open = Event(self, on_open)
|
euporie/core/widgets/forms.py
CHANGED
@@ -236,6 +236,7 @@ class Button:
|
|
236
236
|
|
237
237
|
def default_mouse_handler(self, mouse_event: MouseEvent) -> NotImplementedOrNone:
|
238
238
|
"""Handle mouse events."""
|
239
|
+
app = get_app()
|
239
240
|
if self.disabled():
|
240
241
|
return NotImplemented
|
241
242
|
if mouse_event.button == MouseButton.LEFT:
|
@@ -249,16 +250,15 @@ class Button:
|
|
249
250
|
):
|
250
251
|
y_min, x_min = min(render_info._rowcol_to_yx.values())
|
251
252
|
y_max, x_max = max(render_info._rowcol_to_yx.values())
|
252
|
-
|
253
|
+
app.mouse_limits = WritePosition(
|
253
254
|
xpos=x_min,
|
254
255
|
ypos=y_min,
|
255
|
-
width=x_max - x_min,
|
256
|
-
height=y_max - y_min,
|
256
|
+
width=x_max - x_min + 1,
|
257
|
+
height=y_max - y_min + 1,
|
257
258
|
)
|
258
259
|
self.on_mouse_down.fire()
|
259
260
|
|
260
261
|
if not self.has_focus():
|
261
|
-
app = get_app()
|
262
262
|
app.layout.focus(self)
|
263
263
|
# Invalidate the app - we don't do it by returning None so the
|
264
264
|
# event can bubble
|
@@ -269,7 +269,7 @@ class Button:
|
|
269
269
|
return None
|
270
270
|
|
271
271
|
elif mouse_event.event_type == MouseEventType.MOUSE_UP:
|
272
|
-
|
272
|
+
app.mouse_limits = None
|
273
273
|
if self.selected:
|
274
274
|
self.selected = False
|
275
275
|
self.on_click.fire()
|
@@ -280,11 +280,12 @@ class Button:
|
|
280
280
|
if (info := self.window.render_info) is not None and (
|
281
281
|
info._x_offset + mouse_event.position.x,
|
282
282
|
info._y_offset + mouse_event.position.y,
|
283
|
-
) !=
|
283
|
+
) != app.mouse_position:
|
284
|
+
app.mouse_limits = None
|
284
285
|
self.selected = False
|
285
286
|
return None
|
286
287
|
|
287
|
-
|
288
|
+
app.mouse_limits = None
|
288
289
|
self.selected = False
|
289
290
|
return NotImplemented
|
290
291
|
|
euporie/core/widgets/layout.py
CHANGED
@@ -40,7 +40,7 @@ if TYPE_CHECKING:
|
|
40
40
|
from prompt_toolkit.filters import FilterOrBool
|
41
41
|
from prompt_toolkit.formatted_text.base import AnyFormattedText, StyleAndTextTuples
|
42
42
|
from prompt_toolkit.key_binding.key_bindings import (
|
43
|
-
|
43
|
+
KeyBindingsBase,
|
44
44
|
NotImplementedOrNone,
|
45
45
|
)
|
46
46
|
from prompt_toolkit.layout.containers import AnyContainer, Container, _Split
|
@@ -84,7 +84,7 @@ class Box:
|
|
84
84
|
style: str = "",
|
85
85
|
char: None | str | Callable[[], str] = None,
|
86
86
|
modal: bool = False,
|
87
|
-
key_bindings:
|
87
|
+
key_bindings: KeyBindingsBase | None = None,
|
88
88
|
) -> None:
|
89
89
|
"""Initialize this widget."""
|
90
90
|
if padding is None:
|
@@ -117,7 +117,7 @@ class Box:
|
|
117
117
|
height=height,
|
118
118
|
style=style,
|
119
119
|
modal=modal,
|
120
|
-
key_bindings=
|
120
|
+
key_bindings=key_bindings,
|
121
121
|
)
|
122
122
|
|
123
123
|
def __pt_container__(self) -> Container:
|
@@ -155,7 +155,7 @@ class ConditionalSplit:
|
|
155
155
|
return self._cache.get(vertical, partial(self.load_container, vertical))
|
156
156
|
|
157
157
|
def __pt_container__(self) -> AnyContainer:
|
158
|
-
"""Return a
|
158
|
+
"""Return a dynamic container."""
|
159
159
|
return DynamicContainer(self.container)
|
160
160
|
|
161
161
|
|
@@ -287,7 +287,7 @@ class TabBarControl(UIControl):
|
|
287
287
|
|
288
288
|
def is_focusable(self) -> bool:
|
289
289
|
"""Tell whether this user control is focusable."""
|
290
|
-
return
|
290
|
+
return False
|
291
291
|
|
292
292
|
def create_content(self, width: int, height: int) -> UIContent:
|
293
293
|
"""Generate the formatted text fragments which make the controls output."""
|
euporie/core/widgets/status.py
CHANGED
@@ -77,9 +77,9 @@ class StatusBar:
|
|
77
77
|
) -> None:
|
78
78
|
"""Create a new status bar instance."""
|
79
79
|
self.default: StatusBarFields = default or ([], [])
|
80
|
-
self._status_cache: FastDictCache[
|
81
|
-
|
82
|
-
|
80
|
+
self._status_cache: FastDictCache[tuple[int], list[StyleAndTextTuples]] = (
|
81
|
+
FastDictCache(self._status, size=1)
|
82
|
+
)
|
83
83
|
self.container = ConditionalContainer(
|
84
84
|
content=VSplit(
|
85
85
|
[
|
euporie/hub/app.py
CHANGED
@@ -89,9 +89,13 @@ class HubApp(BaseApp):
|
|
89
89
|
)
|
90
90
|
|
91
91
|
# Import the hubbed app
|
92
|
-
|
93
|
-
|
94
|
-
|
92
|
+
eps = entry_points()
|
93
|
+
if isinstance(eps, dict):
|
94
|
+
points = eps.get("euporie.apps")
|
95
|
+
else:
|
96
|
+
points = eps.select(group="euporie.apps")
|
97
|
+
apps = {x.name: x for x in points} if points else {}
|
98
|
+
if entry_point := apps.get(cls.config.app):
|
95
99
|
app_cls = entry_point.load()
|
96
100
|
else:
|
97
101
|
raise ValueError("Application `%s` not found", cls.config.app)
|
euporie/notebook/app.py
CHANGED
@@ -74,6 +74,8 @@ class NotebookApp(BaseApp):
|
|
74
74
|
notebooks in the terminal.
|
75
75
|
"""
|
76
76
|
|
77
|
+
_tab_container: AnyContainer
|
78
|
+
|
77
79
|
name = "notebook"
|
78
80
|
|
79
81
|
def __init__(self, **kwargs: Any) -> None:
|
@@ -84,8 +86,13 @@ class NotebookApp(BaseApp):
|
|
84
86
|
super().__init__(**kwargs)
|
85
87
|
self.bindings_to_load.append("euporie.notebook.app.NotebookApp")
|
86
88
|
|
89
|
+
self.on_tabs_change += self.set_tab_container
|
90
|
+
|
87
91
|
# Register config hooks
|
88
92
|
self.config.get_item("show_cell_borders").event += lambda x: self.refresh()
|
93
|
+
self.config.get_item("tab_mode").event += self.set_tab_container
|
94
|
+
self.config.get_item("background_pattern").event += self.set_tab_container
|
95
|
+
self.config.get_item("background_character").event += self.set_tab_container
|
89
96
|
|
90
97
|
async def _poll_terminal_colors(self) -> None:
|
91
98
|
"""Repeatedly query the terminal for its background and foreground colours."""
|
@@ -96,10 +103,6 @@ class NotebookApp(BaseApp):
|
|
96
103
|
def pre_run(self, app: Application | None = None) -> None:
|
97
104
|
"""Continue loading the app."""
|
98
105
|
super().pre_run(app)
|
99
|
-
# Ensure an opened tab is focused
|
100
|
-
if self.tab:
|
101
|
-
self.tab.focus()
|
102
|
-
|
103
106
|
# Load style hooks and start polling terminal style
|
104
107
|
if self.config.terminal_polling_interval and hasattr(
|
105
108
|
self.input, "vt100_parser"
|
@@ -118,29 +121,58 @@ class NotebookApp(BaseApp):
|
|
118
121
|
"""Return a container with all opened tabs.
|
119
122
|
|
120
123
|
Returns:
|
121
|
-
A layout displaying the opened tab containers.
|
122
|
-
|
124
|
+
A layout container displaying the opened tab containers.
|
123
125
|
"""
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
126
|
+
try:
|
127
|
+
return self._tab_container
|
128
|
+
except AttributeError:
|
129
|
+
self.set_tab_container()
|
130
|
+
return self._tab_container
|
131
|
+
|
132
|
+
def set_tab_container(self, app: BaseApp | None = None) -> None:
|
133
|
+
"""Set the container to use to display opened tabs."""
|
134
|
+
if not self.tabs:
|
135
|
+
self._tab_container = Pattern(
|
136
|
+
self.config.background_character,
|
137
|
+
self.config.background_pattern,
|
138
|
+
)
|
139
|
+
elif TabMode(self.config.tab_mode) == TabMode.TILE_HORIZONTALLY:
|
140
|
+
self._tab_container = HSplit(
|
141
|
+
children=self.tabs,
|
142
|
+
padding=1,
|
143
|
+
padding_style="class:tab-padding",
|
144
|
+
padding_char="─",
|
145
|
+
)
|
146
|
+
elif TabMode(self.config.tab_mode) == TabMode.TILE_VERTICALLY:
|
147
|
+
self._tab_container = VSplit(
|
148
|
+
children=self.tabs,
|
149
|
+
padding=1,
|
150
|
+
padding_style="class:tab-padding",
|
151
|
+
padding_char="│",
|
152
|
+
)
|
141
153
|
else:
|
142
|
-
|
143
|
-
|
154
|
+
self._tab_container = HSplit(
|
155
|
+
[
|
156
|
+
ConditionalContainer(
|
157
|
+
Window(
|
158
|
+
TabBarControl(
|
159
|
+
tabs=self.tab_bar_tabs,
|
160
|
+
active=lambda: self._tab_idx,
|
161
|
+
closeable=True,
|
162
|
+
),
|
163
|
+
height=2,
|
164
|
+
style="class:app-tab-bar",
|
165
|
+
dont_extend_height=True,
|
166
|
+
),
|
167
|
+
filter=Condition(
|
168
|
+
lambda: (
|
169
|
+
len(self.tabs) > 1 or self.config.always_show_tab_bar
|
170
|
+
)
|
171
|
+
and TabMode(self.config.tab_mode) == TabMode.STACK
|
172
|
+
),
|
173
|
+
),
|
174
|
+
DynamicContainer(lambda: self.tabs[self._tab_idx]),
|
175
|
+
]
|
144
176
|
)
|
145
177
|
|
146
178
|
def _statusbar_defaults(self) -> StatusBarFields | None:
|
@@ -186,24 +218,6 @@ class NotebookApp(BaseApp):
|
|
186
218
|
filter=have_tabs,
|
187
219
|
)
|
188
220
|
|
189
|
-
self.tab_bar_control = TabBarControl(
|
190
|
-
tabs=self.tab_bar_tabs,
|
191
|
-
active=lambda: self._tab_idx,
|
192
|
-
closeable=True,
|
193
|
-
)
|
194
|
-
tab_bar = ConditionalContainer(
|
195
|
-
Window(
|
196
|
-
self.tab_bar_control,
|
197
|
-
height=2,
|
198
|
-
style="class:app-tab-bar",
|
199
|
-
dont_extend_height=True,
|
200
|
-
),
|
201
|
-
filter=Condition(
|
202
|
-
lambda: (len(self.tabs) > 1 or self.config.always_show_tab_bar)
|
203
|
-
and TabMode(self.config.tab_mode) == TabMode.STACK
|
204
|
-
),
|
205
|
-
)
|
206
|
-
|
207
221
|
self.pager = Pager()
|
208
222
|
self.search_bar = SearchBar()
|
209
223
|
|
@@ -255,7 +269,6 @@ class NotebookApp(BaseApp):
|
|
255
269
|
self.side_bar,
|
256
270
|
HSplit(
|
257
271
|
[
|
258
|
-
tab_bar,
|
259
272
|
DynamicContainer(self.tab_container),
|
260
273
|
self.pager,
|
261
274
|
],
|
@@ -500,8 +513,8 @@ class NotebookApp(BaseApp):
|
|
500
513
|
from euporie.notebook.current import get_app
|
501
514
|
|
502
515
|
app = get_app()
|
503
|
-
app.
|
504
|
-
|
516
|
+
app.add_tab(tab := Notebook(app, None))
|
517
|
+
tab.focus()
|
505
518
|
|
506
519
|
@staticmethod
|
507
520
|
@add_cmd()
|