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.
Files changed (92) hide show
  1. euporie/console/__main__.py +3 -1
  2. euporie/console/app.py +6 -4
  3. euporie/console/tabs/console.py +34 -9
  4. euporie/core/__init__.py +6 -1
  5. euporie/core/__main__.py +1 -1
  6. euporie/core/app.py +79 -109
  7. euporie/core/border.py +44 -14
  8. euporie/core/comm/base.py +5 -4
  9. euporie/core/comm/ipywidgets.py +11 -11
  10. euporie/core/comm/registry.py +12 -6
  11. euporie/core/commands.py +30 -23
  12. euporie/core/completion.py +1 -4
  13. euporie/core/config.py +15 -5
  14. euporie/core/convert/{base.py → core.py} +117 -53
  15. euporie/core/convert/formats/ansi.py +46 -25
  16. euporie/core/convert/formats/base64.py +3 -3
  17. euporie/core/convert/formats/common.py +38 -13
  18. euporie/core/convert/formats/formatted_text.py +54 -12
  19. euporie/core/convert/formats/html.py +5 -5
  20. euporie/core/convert/formats/jpeg.py +1 -1
  21. euporie/core/convert/formats/markdown.py +4 -4
  22. euporie/core/convert/formats/pdf.py +1 -1
  23. euporie/core/convert/formats/pil.py +5 -3
  24. euporie/core/convert/formats/png.py +7 -6
  25. euporie/core/convert/formats/rich.py +4 -3
  26. euporie/core/convert/formats/sixel.py +5 -5
  27. euporie/core/convert/utils.py +1 -1
  28. euporie/core/current.py +11 -5
  29. euporie/core/formatted_text/ansi.py +4 -8
  30. euporie/core/formatted_text/html.py +1630 -856
  31. euporie/core/formatted_text/markdown.py +177 -166
  32. euporie/core/formatted_text/table.py +20 -14
  33. euporie/core/formatted_text/utils.py +21 -10
  34. euporie/core/io.py +14 -14
  35. euporie/core/kernel.py +48 -37
  36. euporie/core/key_binding/bindings/micro.py +5 -1
  37. euporie/core/key_binding/bindings/mouse.py +2 -2
  38. euporie/core/keys.py +3 -0
  39. euporie/core/launch.py +5 -2
  40. euporie/core/lexers.py +13 -2
  41. euporie/core/log.py +135 -139
  42. euporie/core/margins.py +32 -14
  43. euporie/core/path.py +273 -0
  44. euporie/core/processors.py +35 -0
  45. euporie/core/renderer.py +21 -5
  46. euporie/core/style.py +34 -19
  47. euporie/core/tabs/base.py +101 -17
  48. euporie/core/tabs/notebook.py +72 -30
  49. euporie/core/terminal.py +56 -48
  50. euporie/core/utils.py +12 -16
  51. euporie/core/widgets/cell.py +6 -5
  52. euporie/core/widgets/cell_outputs.py +2 -2
  53. euporie/core/widgets/decor.py +74 -82
  54. euporie/core/widgets/dialog.py +132 -28
  55. euporie/core/widgets/display.py +76 -24
  56. euporie/core/widgets/file_browser.py +87 -31
  57. euporie/core/widgets/formatted_text_area.py +1 -3
  58. euporie/core/widgets/forms.py +79 -40
  59. euporie/core/widgets/inputs.py +23 -13
  60. euporie/core/widgets/layout.py +4 -3
  61. euporie/core/widgets/menu.py +368 -216
  62. euporie/core/widgets/page.py +99 -58
  63. euporie/core/widgets/pager.py +1 -1
  64. euporie/core/widgets/palette.py +30 -27
  65. euporie/core/widgets/search_bar.py +38 -25
  66. euporie/core/widgets/status_bar.py +103 -5
  67. euporie/data/desktop/euporie-console.desktop +7 -0
  68. euporie/data/desktop/euporie-notebook.desktop +7 -0
  69. euporie/hub/__main__.py +3 -1
  70. euporie/hub/app.py +9 -7
  71. euporie/notebook/__main__.py +3 -1
  72. euporie/notebook/app.py +7 -30
  73. euporie/notebook/tabs/__init__.py +7 -3
  74. euporie/notebook/tabs/display.py +18 -9
  75. euporie/notebook/tabs/edit.py +106 -23
  76. euporie/notebook/tabs/json.py +73 -0
  77. euporie/notebook/tabs/log.py +18 -8
  78. euporie/notebook/tabs/notebook.py +60 -41
  79. euporie/preview/__main__.py +3 -1
  80. euporie/preview/app.py +2 -1
  81. euporie/preview/tabs/notebook.py +23 -10
  82. euporie/web/tabs/web.py +149 -0
  83. euporie/web/widgets/webview.py +563 -0
  84. euporie-2.4.1.data/data/share/applications/euporie-console.desktop +7 -0
  85. euporie-2.4.1.data/data/share/applications/euporie-notebook.desktop +7 -0
  86. {euporie-2.3.2.dist-info → euporie-2.4.1.dist-info}/METADATA +6 -5
  87. euporie-2.4.1.dist-info/RECORD +129 -0
  88. {euporie-2.3.2.dist-info → euporie-2.4.1.dist-info}/WHEEL +1 -1
  89. euporie/core/url.py +0 -64
  90. euporie-2.3.2.dist-info/RECORD +0 -122
  91. {euporie-2.3.2.dist-info → euporie-2.4.1.dist-info}/entry_points.txt +0 -0
  92. {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.base import register
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: UPath | None = None,
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: UPath | None = None,
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: UPath | None = None,
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: UPath | None = None,
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: UPath | None = None,
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: UPath | None = None,
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: UPath | None = None,
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: UPath | None = None,
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: UPath | None = None,
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
- data = data.resize((cols, ceil(cols / w * h)))
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: UPath | None = None,
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: UPath | None = None,
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: UPath | None = None,
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: UPath | None = None,
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: UPath | None = None,
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: UPath | None = None,
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: UPath | None = None,
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: UPath | None = None,
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 = 7,
446
- rows: int = 3,
461
+ cols: int | None = None,
462
+ rows: int | None = None,
447
463
  fg: str | None = None,
448
464
  bg: str | None = None,
449
- path: UPath | None = None,
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: UPath | None = None,
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.base import register
8
+ from euporie.core.convert.core import register
9
9
 
10
10
  if TYPE_CHECKING:
11
- from upath import UPath
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: UPath | None = None,
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: UPath | None = None,
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: UPath | None = None,
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
- if cols is not None:
46
- px, _ = get_app().term_info.cell_size_px
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
- bg = bg or get_app().color_palette.bg.base_hex
49
- if bg is not None:
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: UPath | None = None,
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: UPath | None = None,
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
- config.cell_width, config.cell_height = get_app().term_info.cell_size_px
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.base import register
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: UPath | None = None,
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: UPath | None = None,
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
- browser_css={**_BROWSER_CSS, **_MARKDOWN_CSS},
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: UPath | None = None,
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
- ft = to_formatted_text(markup)
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.base import register
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 upath import UPath
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
- get_lexer_by_name(language),
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: UPath | None = None,
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
@@ -2,7 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from euporie.core.convert.base import register
5
+ from euporie.core.convert.core import register
6
6
  from euporie.core.convert.formats.common import base64_to_bytes_py
7
7
 
8
8
  register(
@@ -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.base import register
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 upath import UPath
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: UPath | None = None,
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: UPath | None = None,
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
@@ -2,7 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from euporie.core.convert.base import register
5
+ from euporie.core.convert.core import register
6
6
  from euporie.core.convert.formats.common import base64_to_bytes_py
7
7
 
8
8
  register(
@@ -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.base import register
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: UPath | None = None,
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.base import register
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: UPath | None = None,
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: UPath | None = None,
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: UPath | None = None,
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: UPath | None = None,
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.base import register
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: UPath | None = None,
27
+ path: Path | None = None,
27
28
  ) -> Markdown:
28
29
  """Convert base64 encoded data to bytes."""
29
30
  from rich.markdown import Markdown