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
@@ -7,18 +7,18 @@ from functools import partial
|
|
7
7
|
from math import ceil
|
8
8
|
from typing import TYPE_CHECKING
|
9
9
|
|
10
|
-
from euporie.core.convert.
|
10
|
+
from euporie.core.convert.core import register
|
11
11
|
from euporie.core.convert.formats.common import chafa_convert_cmd, chafa_convert_py
|
12
12
|
from euporie.core.convert.formats.pil import set_background
|
13
13
|
from euporie.core.convert.utils import call_subproc, commands_exist, have_modules
|
14
14
|
from euporie.core.current import get_app
|
15
15
|
|
16
16
|
if TYPE_CHECKING:
|
17
|
+
from pathlib import Path
|
17
18
|
from typing import Any
|
18
19
|
|
19
20
|
from PIL.Image import Image as PilImage
|
20
21
|
from rich.console import RenderableType
|
21
|
-
from upath import UPath
|
22
22
|
|
23
23
|
log = logging.getLogger(__name__)
|
24
24
|
|
@@ -34,7 +34,7 @@ def html_to_ansi_w3m(
|
|
34
34
|
height: int | None = None,
|
35
35
|
fg: str | None = None,
|
36
36
|
bg: str | None = None,
|
37
|
-
path:
|
37
|
+
path: Path | None = None,
|
38
38
|
) -> str:
|
39
39
|
"""Convert HTML text to formatted ANSI using :command:`w3m`."""
|
40
40
|
cmd: list[Any] = ["w3m", "-T", "text/html"]
|
@@ -54,7 +54,7 @@ def html_to_ansi_elinks(
|
|
54
54
|
height: int | None = None,
|
55
55
|
fg: str | None = None,
|
56
56
|
bg: str | None = None,
|
57
|
-
path:
|
57
|
+
path: Path | None = None,
|
58
58
|
) -> str:
|
59
59
|
"""Convert HTML text to formatted ANSI using :command:`elinks`."""
|
60
60
|
cmd: list[Any] = [
|
@@ -82,7 +82,7 @@ def html_to_ansi_lynx(
|
|
82
82
|
height: int | None = None,
|
83
83
|
fg: str | None = None,
|
84
84
|
bg: str | None = None,
|
85
|
-
path:
|
85
|
+
path: Path | None = None,
|
86
86
|
) -> str:
|
87
87
|
"""Convert HTML text to formatted ANSI using :command:`lynx`."""
|
88
88
|
cmd: list[Any] = ["lynx", "-dump", "-stdin"]
|
@@ -102,7 +102,7 @@ def html_to_ansi_links(
|
|
102
102
|
height: int | None = None,
|
103
103
|
fg: str | None = None,
|
104
104
|
bg: str | None = None,
|
105
|
-
path:
|
105
|
+
path: Path | None = None,
|
106
106
|
) -> str:
|
107
107
|
"""Convert HTML text to formatted ANSI using :command:`links`."""
|
108
108
|
cmd: list[Any] = ["links", "-dump"]
|
@@ -122,7 +122,7 @@ def html_to_ansi_py_htmlparser(
|
|
122
122
|
height: int | None = None,
|
123
123
|
fg: str | None = None,
|
124
124
|
bg: str | None = None,
|
125
|
-
path:
|
125
|
+
path: Path | None = None,
|
126
126
|
) -> str:
|
127
127
|
"""Convert HTML tables to ANSI text using :py:mod:`HTMLParser`."""
|
128
128
|
import io
|
@@ -179,7 +179,7 @@ def latex_to_ansi_py_flatlatex(
|
|
179
179
|
height: int | None = None,
|
180
180
|
fg: str | None = None,
|
181
181
|
bg: str | None = None,
|
182
|
-
path:
|
182
|
+
path: Path | None = None,
|
183
183
|
) -> str:
|
184
184
|
"""Convert LaTeX to ANSI using :py:mod:`flatlatex`."""
|
185
185
|
import flatlatex
|
@@ -198,7 +198,7 @@ def latex_to_ansi_py_pylatexenc(
|
|
198
198
|
height: int | None = None,
|
199
199
|
fg: str | None = None,
|
200
200
|
bg: str | None = None,
|
201
|
-
path:
|
201
|
+
path: Path | None = None,
|
202
202
|
) -> str:
|
203
203
|
"""Convert LaTeX to ANSI using :py:mod:`pylatexenc`."""
|
204
204
|
from pylatexenc.latex2text import LatexNodes2Text
|
@@ -217,7 +217,7 @@ def latex_to_ansi_py_sympy(
|
|
217
217
|
height: int | None = None,
|
218
218
|
fg: str | None = None,
|
219
219
|
bg: str | None = None,
|
220
|
-
path:
|
220
|
+
path: Path | None = None,
|
221
221
|
) -> str:
|
222
222
|
"""Convert LaTeX to ANSI using :py:mod:`sympy`."""
|
223
223
|
from sympy import pretty
|
@@ -241,14 +241,30 @@ def pil_to_ansi_py_timg(
|
|
241
241
|
rows: int | None = None,
|
242
242
|
fg: str | None = None,
|
243
243
|
bg: str | None = None,
|
244
|
-
path:
|
244
|
+
path: Path | None = None,
|
245
245
|
) -> str:
|
246
246
|
"""Convert a PIL image to ANSI text using :py:mod:`timg`."""
|
247
247
|
import timg
|
248
248
|
|
249
|
+
px, py = get_app().term_info.cell_size_px
|
250
|
+
|
251
|
+
# Calculate rows based on image aspect ratio
|
249
252
|
w, h = data.size
|
250
|
-
if cols is not None:
|
251
|
-
|
253
|
+
if rows is None and cols is not None:
|
254
|
+
w, h = data.size
|
255
|
+
rows = ceil(cols / w * h)
|
256
|
+
elif cols is None and rows is not None:
|
257
|
+
w, h = data.size
|
258
|
+
cols = ceil(rows / h * w)
|
259
|
+
elif rows is None and cols is None:
|
260
|
+
cols = ceil(w / px)
|
261
|
+
rows = ceil(h / py)
|
262
|
+
assert rows is not None and cols is not None
|
263
|
+
|
264
|
+
# `timg` assumes a 2x1 terminal cell aspect ratio, so we correct for while
|
265
|
+
# resizing the image
|
266
|
+
data = data.resize((cols, ceil(rows * 2 * (px / py) / 0.5)))
|
267
|
+
|
252
268
|
bg = bg or get_app().color_palette.bg.base_hex
|
253
269
|
if bg:
|
254
270
|
data = set_background(data, bg)
|
@@ -267,7 +283,7 @@ def pil_to_ansi_py_img2unicode(
|
|
267
283
|
rows: int | None = None,
|
268
284
|
fg: str | None = None,
|
269
285
|
bg: str | None = None,
|
270
|
-
path:
|
286
|
+
path: Path | None = None,
|
271
287
|
) -> str:
|
272
288
|
"""Convert a PIL image to ANSI text using :py:mod:`img2unicode`."""
|
273
289
|
import io
|
@@ -305,7 +321,7 @@ def image_to_ansi_timg(
|
|
305
321
|
rows: int | None = None,
|
306
322
|
fg: str | None = None,
|
307
323
|
bg: str | None = None,
|
308
|
-
path:
|
324
|
+
path: Path | None = None,
|
309
325
|
) -> str:
|
310
326
|
"""Convert image data to ANSI text using :command:`timg`."""
|
311
327
|
cmd: list[Any] = ["timg"]
|
@@ -326,7 +342,7 @@ def image_to_ansi_catimg(
|
|
326
342
|
rows: int | None = None,
|
327
343
|
fg: str | None = None,
|
328
344
|
bg: str | None = None,
|
329
|
-
path:
|
345
|
+
path: Path | None = None,
|
330
346
|
) -> str:
|
331
347
|
"""Convert image data to ANSI text using :command:`catimg`."""
|
332
348
|
cmd: list[Any] = ["catimg"]
|
@@ -347,7 +363,7 @@ def image_to_ansi_icat(
|
|
347
363
|
rows: int | None = None,
|
348
364
|
fg: str | None = None,
|
349
365
|
bg: str | None = None,
|
350
|
-
path:
|
366
|
+
path: Path | None = None,
|
351
367
|
) -> str:
|
352
368
|
"""Convert image data to ANSI text using :command:`icat`."""
|
353
369
|
cmd: list[Any] = ["icat"]
|
@@ -368,7 +384,7 @@ def image_to_ansi_tiv(
|
|
368
384
|
rows: int | None = None,
|
369
385
|
fg: str | None = None,
|
370
386
|
bg: str | None = None,
|
371
|
-
path:
|
387
|
+
path: Path | None = None,
|
372
388
|
) -> str:
|
373
389
|
"""Convert image data to ANSI text using :command:`tiv`."""
|
374
390
|
cmd: list[Any] = ["tiv"]
|
@@ -388,7 +404,7 @@ def image_to_ansi_viu(
|
|
388
404
|
rows: int | None = None,
|
389
405
|
fg: str | None = None,
|
390
406
|
bg: str | None = None,
|
391
|
-
path:
|
407
|
+
path: Path | None = None,
|
392
408
|
) -> str:
|
393
409
|
"""Convert image data to ANSI text using :command:`viu`."""
|
394
410
|
cmd: list[Any] = ["viu"]
|
@@ -409,7 +425,7 @@ def image_to_ansi_jp2a(
|
|
409
425
|
rows: int | None = None,
|
410
426
|
fg: str | None = None,
|
411
427
|
bg: str | None = None,
|
412
|
-
path:
|
428
|
+
path: Path | None = None,
|
413
429
|
) -> str:
|
414
430
|
"""Convert image data to ANSI text using :command:`jp2a`."""
|
415
431
|
cmd: list[Any] = ["jp2a", "--color"]
|
@@ -430,7 +446,7 @@ def png_to_ansi_img2txt(
|
|
430
446
|
rows: int | None = None,
|
431
447
|
fg: str | None = None,
|
432
448
|
bg: str | None = None,
|
433
|
-
path:
|
449
|
+
path: Path | None = None,
|
434
450
|
) -> str:
|
435
451
|
"""Convert PNG data to ANSI text using :command:`img2txt`."""
|
436
452
|
cmd: list[Any] = ["img2txt"]
|
@@ -442,15 +458,20 @@ def png_to_ansi_img2txt(
|
|
442
458
|
@register(from_=("png", "jpeg", "svg"), to="ansi", filter_=True, weight=99)
|
443
459
|
def png_to_ansi_py_placeholder(
|
444
460
|
data: bytes,
|
445
|
-
cols: int =
|
446
|
-
rows: int =
|
461
|
+
cols: int | None = None,
|
462
|
+
rows: int | None = None,
|
447
463
|
fg: str | None = None,
|
448
464
|
bg: str | None = None,
|
449
|
-
path:
|
465
|
+
path: Path | None = None,
|
450
466
|
) -> str:
|
451
467
|
"""Draw placeholder ANSI text."""
|
452
468
|
from euporie.core.border import RoundedLine
|
453
469
|
|
470
|
+
if cols is None:
|
471
|
+
cols = 7
|
472
|
+
if rows is None:
|
473
|
+
rows = 3
|
474
|
+
|
454
475
|
lines = []
|
455
476
|
B = RoundedLine.grid
|
456
477
|
lines.append(f"{B.TOP_LEFT}{B.TOP_MID * max(5, (cols - 2))}{B.TOP_RIGHT}")
|
@@ -474,7 +495,7 @@ def rich_to_ansi_py(
|
|
474
495
|
height: int | None = None,
|
475
496
|
fg: str | None = None,
|
476
497
|
bg: str | None = None,
|
477
|
-
path:
|
498
|
+
path: Path | None = None,
|
478
499
|
) -> str:
|
479
500
|
"""Convert rich objects to formatted ANSI text."""
|
480
501
|
import rich
|
@@ -5,10 +5,10 @@ from __future__ import annotations
|
|
5
5
|
import base64
|
6
6
|
from typing import TYPE_CHECKING
|
7
7
|
|
8
|
-
from euporie.core.convert.
|
8
|
+
from euporie.core.convert.core import register
|
9
9
|
|
10
10
|
if TYPE_CHECKING:
|
11
|
-
from
|
11
|
+
from pathlib import Path
|
12
12
|
|
13
13
|
|
14
14
|
@register(
|
@@ -37,7 +37,7 @@ def bytes_to_base64_py(
|
|
37
37
|
height: int | None = None,
|
38
38
|
fg: str | None = None,
|
39
39
|
bg: str | None = None,
|
40
|
-
path:
|
40
|
+
path: Path | None = None,
|
41
41
|
) -> str:
|
42
42
|
"""Convert bytes to base64 encoded data."""
|
43
43
|
if isinstance(data, str):
|
@@ -10,10 +10,10 @@ from euporie.core.convert.utils import call_subproc
|
|
10
10
|
from euporie.core.current import get_app
|
11
11
|
|
12
12
|
if TYPE_CHECKING:
|
13
|
+
from pathlib import Path
|
13
14
|
from typing import Any, Literal
|
14
15
|
|
15
16
|
from PIL.Image import Image as PilImage
|
16
|
-
from upath import UPath
|
17
17
|
|
18
18
|
log = logging.getLogger(__name__)
|
19
19
|
|
@@ -24,7 +24,7 @@ def base64_to_bytes_py(
|
|
24
24
|
height: int | None = None,
|
25
25
|
fg: str | None = None,
|
26
26
|
bg: str | None = None,
|
27
|
-
path:
|
27
|
+
path: Path | None = None,
|
28
28
|
) -> bytes:
|
29
29
|
"""Convert base64 encoded data to bytes."""
|
30
30
|
data_str = data.decode() if isinstance(data, bytes) else data
|
@@ -38,18 +38,21 @@ def imagemagick_convert(
|
|
38
38
|
rows: int | None = None,
|
39
39
|
fg: str | None = None,
|
40
40
|
bg: str | None = None,
|
41
|
-
path:
|
41
|
+
path: Path | None = None,
|
42
42
|
) -> str | bytes:
|
43
43
|
"""Convert image data to PNG bytes using ``imagemagick``."""
|
44
44
|
cmd: list[Any] = ["convert"] # , "-density", "300"]
|
45
|
-
|
46
|
-
|
45
|
+
app = get_app()
|
46
|
+
if cols is not None and hasattr(app, "term_info"):
|
47
|
+
px, _ = app.term_info.cell_size_px
|
47
48
|
cmd += ["-geometry", f"{int(cols * px)}"]
|
48
|
-
|
49
|
-
|
49
|
+
if not bg and hasattr(app, "color_palette"):
|
50
|
+
bg = app.color_palette.bg.base_hex
|
51
|
+
if bg:
|
50
52
|
cmd += ["-background", bg]
|
51
53
|
cmd += ["-[0]", f"{output_format}:-"]
|
52
54
|
result: bytes | str = call_subproc(data, cmd)
|
55
|
+
|
53
56
|
if output_format in {"sixel", "svg"} and isinstance(result, bytes):
|
54
57
|
result = result.decode()
|
55
58
|
return result
|
@@ -62,7 +65,7 @@ def chafa_convert_cmd(
|
|
62
65
|
rows: int | None = None,
|
63
66
|
fg: str | None = None,
|
64
67
|
bg: str | None = None,
|
65
|
-
path:
|
68
|
+
path: Path | None = None,
|
66
69
|
) -> str | bytes:
|
67
70
|
"""Convert image data to ANSI text using :command:`chafa`."""
|
68
71
|
cmd: list[Any] = ["chafa", f"--format={output_format}"]
|
@@ -81,7 +84,7 @@ def chafa_convert_py(
|
|
81
84
|
rows: int | None = None,
|
82
85
|
fg: str | None = None,
|
83
86
|
bg: str | None = None,
|
84
|
-
path:
|
87
|
+
path: Path | None = None,
|
85
88
|
) -> str | bytes:
|
86
89
|
"""Convert image data to ANSI text using ::`chafa.py`."""
|
87
90
|
from chafa.chafa import Canvas, CanvasConfig, PixelMode, PixelType
|
@@ -109,12 +112,34 @@ def chafa_convert_py(
|
|
109
112
|
config = CanvasConfig()
|
110
113
|
# Set output mode
|
111
114
|
config.pixel_mode = str_to_pixel_mode[output_format]
|
112
|
-
# Set canvas height and width
|
113
|
-
config.height = rows or 20
|
114
|
-
config.width = cols or 80
|
115
115
|
# Configure the canvas geometry based on our cell size
|
116
|
-
|
116
|
+
if hasattr(app := get_app(), "term_info"):
|
117
|
+
px, py = app.term_info.cell_size_px
|
118
|
+
else:
|
119
|
+
px, py = 10, 20
|
120
|
+
config.cell_width, config.cell_height = px, py
|
121
|
+
# Set canvas height and width
|
122
|
+
if cols:
|
123
|
+
config.width = cols
|
124
|
+
if rows:
|
125
|
+
config.height = max(1, rows)
|
126
|
+
# If we don't have specified, use the image's aspect
|
127
|
+
else:
|
128
|
+
config.height = max(1, int(cols / data.size[0] * data.size[1] * px / py))
|
129
|
+
|
130
|
+
# Set the foreground color
|
131
|
+
if not fg and hasattr(app, "color_palette"):
|
132
|
+
fg = app.color_palette.fg.base_hex
|
133
|
+
if fg and (color := fg.lstrip("#")):
|
134
|
+
config.fg_color = (
|
135
|
+
int(color[0:2], 16),
|
136
|
+
int(color[2:4], 16),
|
137
|
+
int(color[4:6], 16),
|
138
|
+
)
|
139
|
+
|
117
140
|
# Set the background color
|
141
|
+
if not bg and hasattr(app, "color_palette"):
|
142
|
+
bg = app.color_palette.bg.base_hex
|
118
143
|
if bg and (color := bg.lstrip("#")):
|
119
144
|
config.bg_color = (
|
120
145
|
int(color[0:2], 16),
|
@@ -2,23 +2,27 @@
|
|
2
2
|
|
3
3
|
from __future__ import annotations
|
4
4
|
|
5
|
+
import logging
|
5
6
|
from functools import partial
|
6
7
|
from typing import TYPE_CHECKING
|
7
8
|
|
8
9
|
from prompt_toolkit.cache import SimpleCache
|
9
10
|
from prompt_toolkit.formatted_text import to_formatted_text
|
10
11
|
|
11
|
-
from euporie.core.convert.
|
12
|
+
from euporie.core.convert.core import register
|
12
13
|
from euporie.core.formatted_text.ansi import ANSI
|
13
14
|
from euporie.core.formatted_text.utils import strip_one_trailing_newline
|
14
15
|
from euporie.core.lexers import detect_lexer
|
15
16
|
|
16
17
|
if TYPE_CHECKING:
|
18
|
+
from pathlib import Path
|
19
|
+
|
17
20
|
from prompt_toolkit.formatted_text.base import StyleAndTextTuples
|
18
|
-
from upath import UPath
|
19
21
|
|
20
22
|
from euporie.core.formatted_text.html import HTML, CssSelectors
|
21
23
|
|
24
|
+
log = logging.getLogger(__name__)
|
25
|
+
|
22
26
|
_html_cache: SimpleCache[int, HTML] = SimpleCache(maxsize=20)
|
23
27
|
|
24
28
|
|
@@ -32,7 +36,8 @@ def html_to_ft(
|
|
32
36
|
height: int | None = None,
|
33
37
|
fg: str | None = None,
|
34
38
|
bg: str | None = None,
|
35
|
-
path:
|
39
|
+
path: Path | None = None,
|
40
|
+
css: CssSelectors | None = None,
|
36
41
|
browser_css: CssSelectors | None = None,
|
37
42
|
) -> StyleAndTextTuples:
|
38
43
|
"""Convert markdown to formatted text."""
|
@@ -47,6 +52,7 @@ def html_to_ft(
|
|
47
52
|
width=width,
|
48
53
|
base=path,
|
49
54
|
collapse_root_margin=True,
|
55
|
+
css=css,
|
50
56
|
browser_css=browser_css,
|
51
57
|
),
|
52
58
|
)
|
@@ -71,13 +77,31 @@ def markdown_to_ft(
|
|
71
77
|
height: int | None = None,
|
72
78
|
fg: str | None = None,
|
73
79
|
bg: str | None = None,
|
74
|
-
path:
|
80
|
+
path: Path | None = None,
|
75
81
|
) -> StyleAndTextTuples:
|
76
82
|
"""Convert markdown to formatted text, injecting a custom CSS style-sheet."""
|
77
83
|
from euporie.core.convert.formats.html import markdown_to_html_markdown_it
|
78
|
-
from euporie.core.formatted_text.html import _BROWSER_CSS
|
79
84
|
from euporie.core.formatted_text.markdown import _MARKDOWN_CSS
|
80
85
|
|
86
|
+
css = _MARKDOWN_CSS
|
87
|
+
|
88
|
+
# If we are rendering a file rather than a snippet, apply margins to the root
|
89
|
+
if path is not None:
|
90
|
+
from prompt_toolkit.filters.utils import _always
|
91
|
+
|
92
|
+
from euporie.core.formatted_text.html import CssSelector
|
93
|
+
|
94
|
+
css = {
|
95
|
+
_always: {
|
96
|
+
**_MARKDOWN_CSS[_always],
|
97
|
+
((CssSelector(item="::root"),),): {
|
98
|
+
"max_width": "100em",
|
99
|
+
"margin_left": "auto",
|
100
|
+
"margin_right": "auto",
|
101
|
+
},
|
102
|
+
}
|
103
|
+
}
|
104
|
+
|
81
105
|
return html_to_ft(
|
82
106
|
markdown_to_html_markdown_it(
|
83
107
|
path=path, bg=bg, fg=fg, height=height, width=width, data=data
|
@@ -87,10 +111,18 @@ def markdown_to_ft(
|
|
87
111
|
fg=fg,
|
88
112
|
bg=bg,
|
89
113
|
path=path,
|
90
|
-
|
114
|
+
css=css,
|
91
115
|
)
|
92
116
|
|
93
117
|
|
118
|
+
_BLACKLISTED_LEXERS = {
|
119
|
+
"CBM BASIC V2",
|
120
|
+
"Tera Term macro",
|
121
|
+
"Text only",
|
122
|
+
"GDScript",
|
123
|
+
}
|
124
|
+
|
125
|
+
|
94
126
|
@register(
|
95
127
|
from_="ansi",
|
96
128
|
to="formatted_text",
|
@@ -101,17 +133,27 @@ def ansi_to_ft(
|
|
101
133
|
height: int | None = None,
|
102
134
|
fg: str | None = None,
|
103
135
|
bg: str | None = None,
|
104
|
-
path:
|
136
|
+
path: Path | None = None,
|
105
137
|
) -> StyleAndTextTuples:
|
106
138
|
"""Convert ANSI text to formatted text."""
|
107
139
|
markup = data.decode() if isinstance(data, bytes) else data
|
108
140
|
ft: StyleAndTextTuples
|
109
141
|
if "\x1b" in markup or "\r" in markup:
|
110
142
|
ft = to_formatted_text(ANSI(markup.strip()))
|
111
|
-
elif (lexer := detect_lexer(markup, path)) is not None:
|
112
|
-
from prompt_toolkit.lexers.pygments import _token_cache
|
113
|
-
|
114
|
-
ft = [(_token_cache[t], v) for _, t, v in lexer.get_tokens_unprocessed(markup)]
|
115
143
|
else:
|
116
|
-
|
144
|
+
# Replace tabs with spaces
|
145
|
+
markup = markup.expandtabs()
|
146
|
+
# Use lexer whitelist
|
147
|
+
if (
|
148
|
+
lexer := detect_lexer(markup, path=path)
|
149
|
+
) is not None and lexer.name not in _BLACKLISTED_LEXERS:
|
150
|
+
from prompt_toolkit.lexers.pygments import _token_cache
|
151
|
+
|
152
|
+
log.debug('Lexing output using "%s" lexer', lexer.name)
|
153
|
+
ft = [
|
154
|
+
(_token_cache[t], v) for _, t, v in lexer.get_tokens_unprocessed(markup)
|
155
|
+
]
|
156
|
+
|
157
|
+
else:
|
158
|
+
ft = to_formatted_text(markup)
|
117
159
|
return strip_one_trailing_newline(ft)
|
@@ -11,13 +11,13 @@ from mdit_py_plugins.dollarmath.index import dollarmath_plugin
|
|
11
11
|
from mdit_py_plugins.texmath.index import texmath_plugin
|
12
12
|
from pygments import highlight
|
13
13
|
from pygments.formatters import HtmlFormatter
|
14
|
-
from pygments.lexers import get_lexer_by_name
|
15
14
|
|
16
|
-
from euporie.core.convert.
|
15
|
+
from euporie.core.convert.core import register
|
17
16
|
from euporie.core.current import get_app
|
17
|
+
from euporie.core.lexers import detect_lexer
|
18
18
|
|
19
19
|
if TYPE_CHECKING:
|
20
|
-
from
|
20
|
+
from pathlib import Path
|
21
21
|
|
22
22
|
log = logging.getLogger(__name__)
|
23
23
|
|
@@ -36,7 +36,7 @@ markdown_parser = (
|
|
36
36
|
options_update={
|
37
37
|
"highlight": lambda text, language, lang_args: highlight(
|
38
38
|
text,
|
39
|
-
|
39
|
+
detect_lexer(text, language=language),
|
40
40
|
HtmlFormatter(
|
41
41
|
nowrap=True,
|
42
42
|
noclasses=True,
|
@@ -65,7 +65,7 @@ def markdown_to_html_markdown_it(
|
|
65
65
|
height: int | None = None,
|
66
66
|
fg: str | None = None,
|
67
67
|
bg: str | None = None,
|
68
|
-
path:
|
68
|
+
path: Path | None = None,
|
69
69
|
) -> str:
|
70
70
|
"""Convert markdown to HTML using :py:mod:`markdownit_py`."""
|
71
71
|
assert markdown_parser is not None
|
@@ -5,11 +5,11 @@ from __future__ import annotations
|
|
5
5
|
import logging
|
6
6
|
from typing import TYPE_CHECKING
|
7
7
|
|
8
|
-
from euporie.core.convert.
|
8
|
+
from euporie.core.convert.core import register
|
9
9
|
from euporie.core.convert.utils import have_modules
|
10
10
|
|
11
11
|
if TYPE_CHECKING:
|
12
|
-
from
|
12
|
+
from pathlib import Path
|
13
13
|
|
14
14
|
log = logging.getLogger(__name__)
|
15
15
|
|
@@ -27,7 +27,7 @@ def html_to_markdown_py_html2text(
|
|
27
27
|
height: int | None = None,
|
28
28
|
fg: str | None = None,
|
29
29
|
bg: str | None = None,
|
30
|
-
path:
|
30
|
+
path: Path | None = None,
|
31
31
|
) -> str:
|
32
32
|
"""Convert HTML to markdown tables using :py:mod:`html2text`."""
|
33
33
|
import re
|
@@ -73,7 +73,7 @@ def html_to_markdown_py_mtable(
|
|
73
73
|
height: int | None = None,
|
74
74
|
fg: str | None = None,
|
75
75
|
bg: str | None = None,
|
76
|
-
path:
|
76
|
+
path: Path | None = None,
|
77
77
|
) -> str:
|
78
78
|
"""Convert HTML tables to markdown tables using :py:mod:`mtable`."""
|
79
79
|
from mtable import MarkupTable
|
@@ -5,12 +5,13 @@ from __future__ import annotations
|
|
5
5
|
import logging
|
6
6
|
from typing import TYPE_CHECKING
|
7
7
|
|
8
|
-
from euporie.core.convert.
|
8
|
+
from euporie.core.convert.core import register
|
9
9
|
from euporie.core.convert.utils import have_modules
|
10
10
|
|
11
11
|
if TYPE_CHECKING:
|
12
|
+
from pathlib import Path
|
13
|
+
|
12
14
|
from PIL.Image import Image as PilImage
|
13
|
-
from upath import UPath
|
14
15
|
|
15
16
|
log = logging.getLogger(__name__)
|
16
17
|
|
@@ -43,7 +44,7 @@ def png_to_pil_py(
|
|
43
44
|
rows: int | None = None,
|
44
45
|
fg: str | None = None,
|
45
46
|
bg: str | None = None,
|
46
|
-
path:
|
47
|
+
path: Path | None = None,
|
47
48
|
) -> PilImage:
|
48
49
|
"""Convert PNG to a pillow image using :py:mod:`PIL`."""
|
49
50
|
import io
|
@@ -52,6 +53,7 @@ def png_to_pil_py(
|
|
52
53
|
|
53
54
|
try:
|
54
55
|
image = Image.open(io.BytesIO(data))
|
56
|
+
image.load()
|
55
57
|
except OSError:
|
56
58
|
log.error("Could not load image.")
|
57
59
|
return Image.new(mode="P", size=(1, 1))
|
@@ -5,13 +5,14 @@ from __future__ import annotations
|
|
5
5
|
from functools import partial
|
6
6
|
from typing import TYPE_CHECKING
|
7
7
|
|
8
|
-
from euporie.core.convert.
|
8
|
+
from euporie.core.convert.core import register
|
9
9
|
from euporie.core.convert.formats.common import base64_to_bytes_py, imagemagick_convert
|
10
10
|
from euporie.core.convert.utils import commands_exist, have_modules
|
11
11
|
|
12
12
|
if TYPE_CHECKING:
|
13
|
+
from pathlib import Path
|
14
|
+
|
13
15
|
from PIL.Image import Image as PilImage
|
14
|
-
from upath import UPath
|
15
16
|
|
16
17
|
|
17
18
|
register(
|
@@ -31,7 +32,7 @@ def latex_to_png_dvipng(
|
|
31
32
|
height: int | None = None,
|
32
33
|
fg: str | None = None,
|
33
34
|
bg: str | None = None,
|
34
|
-
path:
|
35
|
+
path: Path | None = None,
|
35
36
|
) -> bytes | None:
|
36
37
|
"""Render LaTeX as a png image using :command:`dvipng`.
|
37
38
|
|
@@ -112,7 +113,7 @@ def latex_to_png_py_mpl(
|
|
112
113
|
height: int | None = None,
|
113
114
|
fg: str | None = None,
|
114
115
|
bg: str | None = None,
|
115
|
-
path:
|
116
|
+
path: Path | None = None,
|
116
117
|
) -> bytes:
|
117
118
|
"""Render LaTeX as a png image using :py:module:`matplotlib`.
|
118
119
|
|
@@ -155,7 +156,7 @@ def pil_to_png_py_pil(
|
|
155
156
|
rows: int | None = None,
|
156
157
|
fg: str | None = None,
|
157
158
|
bg: str | None = None,
|
158
|
-
path:
|
159
|
+
path: Path | None = None,
|
159
160
|
) -> bytes:
|
160
161
|
"""Convert a pillow image to sixels :py:mod:`teimpy`."""
|
161
162
|
import io
|
@@ -177,7 +178,7 @@ def svg_to_png_py_cairosvg(
|
|
177
178
|
height: int | None = None,
|
178
179
|
fg: str | None = None,
|
179
180
|
bg: str | None = None,
|
180
|
-
path:
|
181
|
+
path: Path | None = None,
|
181
182
|
) -> str:
|
182
183
|
"""Convert SVG to PNG using :py:mod:`cairosvg`."""
|
183
184
|
import cairosvg
|
@@ -4,12 +4,13 @@ from __future__ import annotations
|
|
4
4
|
|
5
5
|
from typing import TYPE_CHECKING
|
6
6
|
|
7
|
-
from euporie.core.convert.
|
7
|
+
from euporie.core.convert.core import register
|
8
8
|
from euporie.core.convert.utils import have_modules
|
9
9
|
|
10
10
|
if TYPE_CHECKING:
|
11
|
+
from pathlib import Path
|
12
|
+
|
11
13
|
from rich.markdown import Markdown
|
12
|
-
from upath import UPath
|
13
14
|
|
14
15
|
|
15
16
|
@register(
|
@@ -23,7 +24,7 @@ def markdown_to_rich_py(
|
|
23
24
|
height: int | None = None,
|
24
25
|
fg: str | None = None,
|
25
26
|
bg: str | None = None,
|
26
|
-
path:
|
27
|
+
path: Path | None = None,
|
27
28
|
) -> Markdown:
|
28
29
|
"""Convert base64 encoded data to bytes."""
|
29
30
|
from rich.markdown import Markdown
|