PyTermGUI 7.7.4__tar.gz → 7.8.0__tar.gz

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 (78) hide show
  1. {pytermgui-7.7.4 → pytermgui-7.8.0}/CHANGELOG.md +32 -2
  2. {pytermgui-7.7.4 → pytermgui-7.8.0}/PKG-INFO +44 -18
  3. {pytermgui-7.7.4 → pytermgui-7.8.0}/README.md +19 -17
  4. {pytermgui-7.7.4 → pytermgui-7.8.0}/pyproject.toml +13 -4
  5. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/__init__.py +1 -1
  6. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/animations.py +1 -1
  7. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/ansi_interface.py +1 -1
  8. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/colors.py +3 -3
  9. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/exporters.py +2 -2
  10. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/file_loaders.py +4 -4
  11. pytermgui-7.8.0/pytermgui/helpers.py +60 -0
  12. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/input.py +1 -5
  13. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/inspector.py +1 -1
  14. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/markup/macros.py +1 -1
  15. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/markup/parsing.py +11 -5
  16. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/palettes.py +5 -10
  17. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/regex.py +6 -2
  18. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/term.py +50 -19
  19. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/widgets/base.py +0 -12
  20. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/widgets/containers.py +47 -4
  21. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/widgets/input_field.py +1 -2
  22. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/window_manager/compositor.py +3 -1
  23. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/window_manager/manager.py +10 -3
  24. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/window_manager/window.py +3 -1
  25. pytermgui-7.7.4/examples/README.md +0 -19
  26. pytermgui-7.7.4/pytermgui/helpers.py +0 -113
  27. pytermgui-7.7.4/tests/__init__.py +0 -0
  28. pytermgui-7.7.4/tests/_exporter_targets.py +0 -1646
  29. pytermgui-7.7.4/tests/colorgrids.py +0 -122
  30. pytermgui-7.7.4/tests/test.json +0 -45
  31. pytermgui-7.7.4/tests/test.yaml +0 -103
  32. pytermgui-7.7.4/tests/test_animations.py +0 -114
  33. pytermgui-7.7.4/tests/test_auto.py +0 -68
  34. pytermgui-7.7.4/tests/test_colors.py +0 -113
  35. pytermgui-7.7.4/tests/test_dump_n_load.py +0 -34
  36. pytermgui-7.7.4/tests/test_exporters.py +0 -1063
  37. pytermgui-7.7.4/tests/test_fancy_repr.py +0 -17
  38. pytermgui-7.7.4/tests/test_helpers.py +0 -53
  39. pytermgui-7.7.4/tests/test_highlight_markup_literal.py +0 -8
  40. pytermgui-7.7.4/tests/test_layouts.py +0 -99
  41. pytermgui-7.7.4/tests/test_parser.py +0 -179
  42. pytermgui-7.7.4/tests/test_regex.py +0 -40
  43. pytermgui-7.7.4/tests/test_styles.py +0 -56
  44. {pytermgui-7.7.4 → pytermgui-7.8.0}/.gitignore +0 -0
  45. {pytermgui-7.7.4 → pytermgui-7.8.0}/LICENSE +0 -0
  46. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/cmd.py +0 -0
  47. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/color_info.py +0 -0
  48. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/context_managers.py +0 -0
  49. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/enums.py +0 -0
  50. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/exceptions.py +0 -0
  51. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/fancy_repr.py +0 -0
  52. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/highlighters.py +0 -0
  53. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/markup/__init__.py +0 -0
  54. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/markup/aliases.py +0 -0
  55. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/markup/language.py +0 -0
  56. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/markup/style_maps.py +0 -0
  57. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/markup/tokens.py +0 -0
  58. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/prettifiers.py +0 -0
  59. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/pretty.py +0 -0
  60. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/py.typed +0 -0
  61. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/serialization.py +0 -0
  62. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/widgets/__init__.py +0 -0
  63. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/widgets/boxes.py +0 -0
  64. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/widgets/button.py +0 -0
  65. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/widgets/checkbox.py +0 -0
  66. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/widgets/collapsible.py +0 -0
  67. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/widgets/color_picker.py +0 -0
  68. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/widgets/fancy_repr.py +0 -0
  69. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/widgets/frames.py +0 -0
  70. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/widgets/inline.py +0 -0
  71. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/widgets/keyboard_button.py +0 -0
  72. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/widgets/pixel_matrix.py +0 -0
  73. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/widgets/slider.py +0 -0
  74. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/widgets/styles.py +0 -0
  75. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/widgets/toggle.py +0 -0
  76. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/win32console.py +0 -0
  77. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/window_manager/__init__.py +0 -0
  78. {pytermgui-7.7.4 → pytermgui-7.8.0}/pytermgui/window_manager/layouts.py +0 -0
@@ -1,11 +1,39 @@
1
- ## [7.7.3] - 2024-12-29
1
+ ## [7.8.0] - 2026-08-10
2
+
3
+ ### Additions
4
+
5
+ - Add grapheme-aware wrapping with correct widths for CJK and emoji sequences
6
+ - Preserve ANSI styles and hyperlinks when wrapping text
7
+ - Document terminal compatibility and the project's archived status
2
8
 
3
9
  ### Bugfixes
4
10
 
5
- - Fix getch mapping for Windows keys
11
+ - Fix terminal clearing, synchronized frame recording, and resize handling
12
+ - Fix multiline input cursor placement and nested container scrolling
13
+ - Fix uneven splitter columns and automatically sized scrollable windows
14
+ - Fix ANSI bright magenta, cyan, and white color mappings
15
+ - Fix cursor-home and erase sequences in ANSI exports
16
+ - Avoid busy-waiting for input on Windows
17
+
18
+ ### Maintenance
19
+
20
+ - Modernize linting and packaging configuration
21
+ - Remove obsolete project assets and tooling
6
22
 
7
23
  <!-- HATCH README END -->
8
24
 
25
+ ## [7.7.4] - 2025-03-31
26
+
27
+ ### Bugfixes
28
+
29
+ - Fix rapid input to \_GetchLinux
30
+
31
+ ## [7.7.3] - 2024-12-29
32
+
33
+ ### Bugfixes
34
+
35
+ - Fix getch mapping for Windows keys
36
+
9
37
  ## [7.7.2] - 2024-08-31
10
38
 
11
39
  ### Additions
@@ -416,6 +444,8 @@
416
444
 
417
445
  <!-- HATCH URI DEFINITIONS START -->
418
446
 
447
+ [7.8.0]: https://github.com/bczsalba/pytermgui/compare/v7.7.4...v7.8.0
448
+ [7.7.4]: https://github.com/bczsalba/pytermgui/compare/v7.7.3...v7.7.4
419
449
  [7.7.3]: https://github.com/bczsalba/pytermgui/compare/v7.7.2...v7.7.3
420
450
  [7.7.2]: https://github.com/bczsalba/pytermgui/compare/v7.7.1...v7.7.2
421
451
  [7.7.1]: https://github.com/bczsalba/pytermgui/compare/v7.7.0...v7.7.1
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyTermGUI
3
- Version: 7.7.4
3
+ Version: 7.8.0
4
4
  Summary: Python TUI framework with mouse support, modular widget system, customizable and rapid terminal markup language and more!
5
5
  Project-URL: homepage, https://github.com/bczsalba/PyTermGUI
6
6
  Project-URL: repository, https://github.com/bczsalba/PyTermGUI
@@ -26,13 +26,17 @@ Classifier: Topic :: Text Processing :: Markup
26
26
  Classifier: Typing :: Typed
27
27
  Requires-Python: >=3.8
28
28
  Requires-Dist: typing-extensions
29
- Requires-Dist: wcwidth
29
+ Requires-Dist: wcwidth>=0.5
30
+ Provides-Extra: docs
31
+ Requires-Dist: mkdocs-gen-files; extra == 'docs'
32
+ Requires-Dist: mkdocs-literate-nav; extra == 'docs'
33
+ Requires-Dist: mkdocs-material; extra == 'docs'
34
+ Requires-Dist: mkdocstrings[python]; extra == 'docs'
35
+ Requires-Dist: termage; extra == 'docs'
30
36
  Provides-Extra: yaml
31
37
  Requires-Dist: pyyaml; extra == 'yaml'
32
38
  Description-Content-Type: text/markdown
33
39
 
34
- <!--![title](https://github.com/bczsalba/pytermgui/raw/master/assets/title.png)-->
35
-
36
40
  ![title](https://github.com/bczsalba/pytermgui/raw/master/assets/readme/screenshot.png)
37
41
 
38
42
  > Python TUI framework with mouse support, modular widget system, customizable and rapid terminal markup language and more!
@@ -45,26 +49,20 @@ pip3 install pytermgui
45
49
  <a href="https://pypi.org/project/pytermgui">
46
50
  <img alt="PyPi project" src="https://img.shields.io/pypi/v/pytermgui?color=brightgreen">
47
51
  </a>
48
- <a href="https://github.com/bczsalba/pytermgui/blob/master/utils/create_badge.py">
49
- <img alt="Code quality" src="https://raw.githubusercontent.com/bczsalba/pytermgui/master/assets/badges/quality.svg">
50
- </a>
51
- <a href="http://ptg.bczsalba.com/">
52
- <img src="https://img.shields.io/badge/documentation-up%20to%20date-brightgreen">
52
+ <a href="https://ptg.bczsalba.com/">
53
+ <img src="https://img.shields.io/badge/documentation-gray">
53
54
  </a>
54
55
  <a href="https://github.com/bczsalba/pytermgui/actions/workflows/pytest.yml">
55
56
  <img src="https://github.com/bczsalba/pytermgui/actions/workflows/pytest.yml/badge.svg">
56
57
  </a>
57
58
  </p>
58
- <p align=center>
59
- <a href="https://discord.gg/g4bqMvpG4U">
60
- <img src="https://img.shields.io/discord/999374285686706367?label=join%20our%20discord">
61
- </a>
62
59
  </p>
63
60
 
64
- ## Notice
61
+ <hr>
62
+
63
+ ## Archival notice
65
64
 
66
- A much better, more complete version of PTG's core ideas now exists over at [Shade 40](https://github.com/shade40). While PTG is not yet fully obsolete,
67
- those libraries will be the primary focus of development going forward.
65
+ PyTermGUI is no longer in development. The latest release will remain available, alongside its documentation. The MIT license allows for free modification of the library, as long as you include credit to its original author. Thank you for everything.
68
66
 
69
67
  <hr>
70
68
 
@@ -97,19 +95,47 @@ Additionally, there are a couple of neat tools to make your general Python devel
97
95
  - A pretty printer for both the REPL and IPython
98
96
  - A way to create SVG and HTML screenshots of your terminal
99
97
 
98
+ ### Terminal compatibility
99
+
100
+ PyTermGUI relies on ANSI virtual-terminal sequences for rendering, cursor reports,
101
+ keyboard input, and mouse input. On Windows, support depends on the terminal emulator's
102
+ ANSI compatibility. [Windows Terminal](https://github.com/microsoft/terminal) supports
103
+ the expected cursor and mouse protocols, while the legacy Windows Console Host
104
+ (`conhost.exe`) may provide reduced functionality. This does not depend on whether
105
+ PowerShell or Command Prompt is running inside the emulator. Mouse support also depends
106
+ on the emulator forwarding the requested events.
107
+
100
108
  ## Latest release
101
109
 
102
- ### [7.7.3] - 2024-12-29
110
+ ### [7.8.0] - 2026-08-10
111
+
112
+ ### Additions
113
+
114
+ - Add grapheme-aware wrapping with correct widths for CJK and emoji sequences
115
+ - Preserve ANSI styles and hyperlinks when wrapping text
116
+ - Document terminal compatibility and the project's archived status
103
117
 
104
118
  ### Bugfixes
105
119
 
106
- - Fix getch mapping for Windows keys
120
+ - Fix terminal clearing, synchronized frame recording, and resize handling
121
+ - Fix multiline input cursor placement and nested container scrolling
122
+ - Fix uneven splitter columns and automatically sized scrollable windows
123
+ - Fix ANSI bright magenta, cyan, and white color mappings
124
+ - Fix cursor-home and erase sequences in ANSI exports
125
+ - Avoid busy-waiting for input on Windows
126
+
127
+ ### Maintenance
128
+
129
+ - Modernize linting and packaging configuration
130
+ - Remove obsolete project assets and tooling
107
131
 
108
132
 
109
133
  Read the full changelog [here](https://github.com/bczsalba/pytermgui/blob/master/CHANGELOG.md).
110
134
 
111
135
 
112
136
 
137
+ [7.8.0]: https://github.com/bczsalba/pytermgui/compare/v7.7.4...v7.8.0
138
+ [7.7.4]: https://github.com/bczsalba/pytermgui/compare/v7.7.3...v7.7.4
113
139
  [7.7.3]: https://github.com/bczsalba/pytermgui/compare/v7.7.2...v7.7.3
114
140
  [7.7.2]: https://github.com/bczsalba/pytermgui/compare/v7.7.1...v7.7.2
115
141
  [7.7.1]: https://github.com/bczsalba/pytermgui/compare/v7.7.0...v7.7.1
@@ -1,5 +1,3 @@
1
- <!--![title](https://github.com/bczsalba/pytermgui/raw/master/assets/title.png)-->
2
-
3
1
  ![title](https://github.com/bczsalba/pytermgui/raw/master/assets/readme/screenshot.png)
4
2
 
5
3
  > Python TUI framework with mouse support, modular widget system, customizable and rapid terminal markup language and more!
@@ -12,26 +10,20 @@ pip3 install pytermgui
12
10
  <a href="https://pypi.org/project/pytermgui">
13
11
  <img alt="PyPi project" src="https://img.shields.io/pypi/v/pytermgui?color=brightgreen">
14
12
  </a>
15
- <a href="https://github.com/bczsalba/pytermgui/blob/master/utils/create_badge.py">
16
- <img alt="Code quality" src="https://raw.githubusercontent.com/bczsalba/pytermgui/master/assets/badges/quality.svg">
17
- </a>
18
- <a href="http://ptg.bczsalba.com/">
19
- <img src="https://img.shields.io/badge/documentation-up%20to%20date-brightgreen">
13
+ <a href="https://ptg.bczsalba.com/">
14
+ <img src="https://img.shields.io/badge/documentation-gray">
20
15
  </a>
21
16
  <a href="https://github.com/bczsalba/pytermgui/actions/workflows/pytest.yml">
22
17
  <img src="https://github.com/bczsalba/pytermgui/actions/workflows/pytest.yml/badge.svg">
23
18
  </a>
24
19
  </p>
25
- <p align=center>
26
- <a href="https://discord.gg/g4bqMvpG4U">
27
- <img src="https://img.shields.io/discord/999374285686706367?label=join%20our%20discord">
28
- </a>
29
20
  </p>
30
21
 
31
- ## Notice
22
+ <hr>
32
23
 
33
- A much better, more complete version of PTG's core ideas now exists over at [Shade 40](https://github.com/shade40). While PTG is not yet fully obsolete,
34
- those libraries will be the primary focus of development going forward.
24
+ ## Archival notice
25
+
26
+ PyTermGUI is no longer in development. The latest release will remain available, alongside its documentation. The MIT license allows for free modification of the library, as long as you include credit to its original author. Thank you for everything.
35
27
 
36
28
  <hr>
37
29
 
@@ -64,6 +56,16 @@ Additionally, there are a couple of neat tools to make your general Python devel
64
56
  - A pretty printer for both the REPL and IPython
65
57
  - A way to create SVG and HTML screenshots of your terminal
66
58
 
59
+ ### Terminal compatibility
60
+
61
+ PyTermGUI relies on ANSI virtual-terminal sequences for rendering, cursor reports,
62
+ keyboard input, and mouse input. On Windows, support depends on the terminal emulator's
63
+ ANSI compatibility. [Windows Terminal](https://github.com/microsoft/terminal) supports
64
+ the expected cursor and mouse protocols, while the legacy Windows Console Host
65
+ (`conhost.exe`) may provide reduced functionality. This does not depend on whether
66
+ PowerShell or Command Prompt is running inside the emulator. Mouse support also depends
67
+ on the emulator forwarding the requested events.
68
+
67
69
  <!-- HATCH README END -->
68
70
 
69
71
  ## Examples
@@ -181,9 +183,9 @@ We use algorithms based on human vision to convert and downgrade colors when the
181
183
 
182
184
  ## Questions? See the docs!
183
185
 
184
- Pretty much every single name in the library, private or public, has an insightful dockstring attached to it, and we are accumulating a growing amount of walkthrough-based documentations articles. See 'em all on the [doc website](https://ptg.bczsalba.com)!
186
+ Pretty much every name in the library, private or public, has an insightful docstring attached to it, alongside walkthrough-based documentation articles. See them all on the [documentation website](https://ptg.bczsalba.com)!
185
187
 
186
188
 
187
- ## Contributions, issues et al.
189
+ ## Project status
188
190
 
189
- If you have any problems using the library, feel free to open up a discussion or raise an issue ticket. If you would prefer to hack on the library yourself, see the [contribution guidelines](https://github.com/bczsalba/pytermgui/blob/master/CONTRIBUTING.md). Pull requests are encouraged, but make sure you aren't trying to fix an issue that others are already working on, for your own sake. :slightly_smiling_face:
191
+ PyTermGUI has reached its final release and is no longer under active development. The repository and its documentation are retained as an archive for existing users.
@@ -13,7 +13,7 @@ description = """\
13
13
  license = {text = "MIT"}
14
14
 
15
15
  requires-python = ">=3.8"
16
- dependencies = ["wcwidth", "typing_extensions"]
16
+ dependencies = ["wcwidth>=0.5", "typing_extensions"]
17
17
 
18
18
  keywords = [
19
19
  "terminal",
@@ -53,6 +53,13 @@ documentation = "https://ptg.bczsalba.com"
53
53
 
54
54
  [project.optional-dependencies]
55
55
  yaml = ["PyYAML"]
56
+ docs = [
57
+ "mkdocs-material",
58
+ "mkdocs-gen-files",
59
+ "mkdocs-literate-nav",
60
+ "mkdocstrings[python]",
61
+ "termage",
62
+ ]
56
63
 
57
64
  [project.scripts]
58
65
  ptg = "pytermgui.cmd:main"
@@ -63,9 +70,8 @@ path = "pytermgui/__init__.py"
63
70
  [tool.hatch.build]
64
71
  include = [
65
72
  "/pytermgui",
66
- "/tests",
67
- "README.md",
68
- "CHANGELOG.md",
73
+ "/README.md",
74
+ "/CHANGELOG.md",
69
75
  ]
70
76
 
71
77
  [tool.hatch.metadata.hooks.fancy-pypi-readme]
@@ -96,6 +102,9 @@ start-after = "\n<!-- HATCH URI DEFINITIONS START -->"
96
102
  [tool.mypy]
97
103
  show_error_codes = true
98
104
 
105
+ [tool.pylint.main]
106
+ py-version = "3.8"
107
+
99
108
  [tool.pylint.messages_control]
100
109
  disable = [
101
110
  "fixme",
@@ -40,7 +40,7 @@ if "-m" in sys.argv: # pragma: no cover
40
40
 
41
41
  warnings.filterwarnings("ignore")
42
42
 
43
- __version__ = "7.7.4"
43
+ __version__ = "7.8.0"
44
44
 
45
45
 
46
46
  def auto(data: Any, **widget_args: Any) -> Optional[Widget | list[Splitter]]:
@@ -23,7 +23,7 @@ from typing import TYPE_CHECKING, Any, Callable
23
23
  if TYPE_CHECKING:
24
24
  from .widgets import Widget
25
25
  else:
26
- Widget = Any
26
+ Widget = Any # pylint: disable=invalid-name
27
27
 
28
28
  __all__ = ["Animator", "FloatAnimation", "AttrAnimation", "animator", "is_animated"]
29
29
 
@@ -116,7 +116,7 @@ def clear(what: str = "screen") -> None:
116
116
  commands = {
117
117
  "eos": "\x1b[0J",
118
118
  "bos": "\x1b[1J",
119
- "screen": "\x1b[2J",
119
+ "screen": "\x1b[H\x1b[2J",
120
120
  "eol": "\x1b[0K",
121
121
  "bol": "\x1b[1K",
122
122
  "line": "\x1b[2K",
@@ -68,9 +68,9 @@ XTERM_NAMED_COLORS = {
68
68
  10: "ansi-bright-green",
69
69
  11: "ansi-bright-yellow",
70
70
  12: "ansi-bright-blue",
71
- 14: "ansi-bright-magenta",
72
- 15: "ansi-bright-cyan",
73
- 16: "ansi-bright-white",
71
+ 13: "ansi-bright-magenta",
72
+ 14: "ansi-bright-cyan",
73
+ 15: "ansi-bright-white",
74
74
  }
75
75
 
76
76
  NAMED_COLORS = {
@@ -273,7 +273,7 @@ def token_to_css(token: Token, invert: bool = False) -> str:
273
273
 
274
274
  # We take this many arguments for future proofing and customization, not much we can
275
275
  # do about it.
276
- def to_html( # pylint: disable=too-many-arguments, too-many-locals
276
+ def to_html( # pylint: disable=too-many-arguments, too-many-locals, R0917
277
277
  obj: Widget | StyledText | str,
278
278
  prefix: str | None = None,
279
279
  inline_styles: bool = False,
@@ -441,7 +441,7 @@ def _make_tag(tagname: str, content: str = "", **attrs) -> str:
441
441
 
442
442
  # This is a bit of a beast of a function, but it does the job and IMO reducing it
443
443
  # into parts would just make our lives more complicated.
444
- def to_svg( # pylint: disable=too-many-locals, too-many-arguments, too-many-statements, R0912
444
+ def to_svg( # pylint: disable=too-many-locals, too-many-arguments, too-many-statements, R0912, R0917
445
445
  obj: Widget | StyledText | str,
446
446
  prefix: str | None = None,
447
447
  chrome: bool = True,
@@ -131,14 +131,14 @@ from . import widgets as widgets_m
131
131
  from .markup import tim
132
132
  from .serialization import Serializer
133
133
 
134
- YAML_ERROR = None
134
+ _yaml_error = None
135
135
 
136
136
  try:
137
137
  import yaml
138
138
  except ImportError as import_error:
139
139
  # yaml is explicitly checked to be None later
140
140
  yaml = None # type: ignore
141
- YAML_ERROR = import_error
141
+ _yaml_error = import_error
142
142
 
143
143
 
144
144
  __all__ = ["WidgetNamespace", "FileLoader", "YamlLoader", "JsonLoader"]
@@ -412,10 +412,10 @@ class YamlLoader(FileLoader):
412
412
  def __init__(self, serializer: Serializer | None = None) -> None:
413
413
  """Initialize object, check for installation of PyYAML."""
414
414
 
415
- if YAML_ERROR is not None:
415
+ if _yaml_error is not None:
416
416
  raise RuntimeError(
417
417
  "YAML implementation module not found. Please install `PyYAML` to use `YamlLoader`."
418
- ) from YAML_ERROR
418
+ ) from _yaml_error
419
419
 
420
420
  super().__init__()
421
421
 
@@ -0,0 +1,60 @@
1
+ """Helper methods and functions for pytermgui."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Iterator
6
+
7
+ from wcwidth import wrap as wcwidth_wrap
8
+
9
+ from .regex import real_length
10
+
11
+ __all__ = [
12
+ "break_line",
13
+ ]
14
+
15
+
16
+ def break_line(
17
+ line: str, limit: int, non_first_limit: int | None = None, fill: str | None = None
18
+ ) -> Iterator[str]:
19
+ """Breaks a line into a `list[str]` with maximum `limit` length per line.
20
+
21
+ Uses wcwidth.wrap() for proper word-boundary breaking, grapheme cluster
22
+ handling, and wide character support. ANSI sequences are preserved and
23
+ propagated across line breaks.
24
+
25
+ Args:
26
+ line: The line to split. May or may not contain ANSI sequences.
27
+ limit: The maximum amount of characters allowed in each line, excluding
28
+ non-printing sequences.
29
+ non_first_limit: The limit after the first line. If not given, defaults
30
+ to `limit`.
31
+ fill: Optional character to pad lines to the limit width.
32
+ """
33
+
34
+ if line in ["", "\x1b[0m"]:
35
+ yield ""
36
+ return
37
+
38
+ def _pad_line(text: str, width: int) -> str:
39
+ if fill is None:
40
+ return text
41
+
42
+ count = width - real_length(text)
43
+ if count > 0:
44
+ return text + count * fill
45
+ return text
46
+
47
+ if non_first_limit is None:
48
+ non_first_limit = limit
49
+
50
+ for segment in line.split("\n"):
51
+ if not segment:
52
+ yield _pad_line("", limit)
53
+ limit = non_first_limit
54
+ continue
55
+
56
+ wrapped = wcwidth_wrap(segment, limit)
57
+
58
+ for wrapped_line in wrapped:
59
+ yield _pad_line(wrapped_line, limit)
60
+ limit = non_first_limit
@@ -5,10 +5,6 @@ Credits:
5
5
 
6
6
  - Original getch implementation: [Danny Yoo](https://code.activestate.com/recipes/134892)
7
7
  - Modern additions & idea: [kcsaff](https://github.com/kcsaff/getkey)
8
-
9
- Note that the original link seems to no longer be active, but an archive can be found
10
- on [GitHub](https://github.com/ActiveState/code/tree/master/recipes/Python/
11
- 134892_getchlike_unbuffered_character_reading_stdboth).
12
8
  """
13
9
 
14
10
  # pylint doesn't see the C source
@@ -134,7 +130,7 @@ class _GetchUnix:
134
130
 
135
131
  descriptor = sys.stdin.fileno()
136
132
  old_settings = termios.tcgetattr(descriptor)
137
- tty.setcbreak(descriptor)
133
+ tty.setcbreak(descriptor, termios.TCSANOW)
138
134
 
139
135
  try:
140
136
  yield self._read(1)
@@ -175,7 +175,7 @@ def inspect(target: object, **inspector_args: Any) -> Inspector:
175
175
  class Inspector(Container):
176
176
  """A widget to inspect any Python object."""
177
177
 
178
- def __init__( # pylint: disable=too-many-arguments
178
+ def __init__( # pylint: disable=too-many-arguments, R0917
179
179
  self,
180
180
  target: object = None,
181
181
  show_private: bool = False,
@@ -9,7 +9,7 @@ from .parsing import parse
9
9
 
10
10
  DEFAULT_MACROS = {}
11
11
 
12
- MarkupLanguage = Any
12
+ MarkupLanguage = Any # pylint: disable=invalid-name
13
13
 
14
14
 
15
15
  MacroTemplate = TypeVar("MacroTemplate")
@@ -211,6 +211,11 @@ def tokenize_ansi( # pylint: disable=too-many-locals, too-many-branches, too-ma
211
211
  if cursor < start:
212
212
  yield PlainToken(text[cursor:start])
213
213
 
214
+ if matchobj.groups()[4] is not None:
215
+ cursor = end
216
+ yield ClearToken("/~")
217
+ continue
218
+
214
219
  if link_osc != (None, None):
215
220
  cursor = end
216
221
  uri, label = link_osc
@@ -225,6 +230,9 @@ def tokenize_ansi( # pylint: disable=too-many-locals, too-many-branches, too-ma
225
230
 
226
231
  cursor = end
227
232
 
233
+ if full.endswith(("J", "K")):
234
+ continue
235
+
228
236
  code = ""
229
237
 
230
238
  # Position
@@ -233,9 +241,7 @@ def tokenize_ansi( # pylint: disable=too-many-locals, too-many-branches, too-ma
233
241
  if posmatch is not None:
234
242
  ypos, xpos = posmatch.groups()
235
243
  if not ypos and not xpos:
236
- raise ValueError(
237
- f"Cannot parse cursor when no position is supplied. Match: {posmatch!r}"
238
- )
244
+ ypos = xpos = "1"
239
245
 
240
246
  yield CursorToken(content, int(ypos) or None, int(xpos) or None)
241
247
  continue
@@ -481,7 +487,7 @@ def optimize_tokens(tokens: list[Token]) -> Iterator[Token]:
481
487
  applied.append(tkn)
482
488
  yield tkn
483
489
 
484
- def _remove_redundant_color(token: Token) -> None:
490
+ def _remove_redundant_color(token: Token, new: Color) -> None:
485
491
  """Removes non-functional colors.
486
492
 
487
493
  These happen in the following ways:
@@ -513,7 +519,7 @@ def optimize_tokens(tokens: list[Token]) -> Iterator[Token]:
513
519
  if Token.is_color(token):
514
520
  new = token.color
515
521
 
516
- _remove_redundant_color(token)
522
+ _remove_redundant_color(token, new)
517
523
 
518
524
  if not any(token.markup == applied.markup for applied in current_tag_group):
519
525
  current_tag_group.append(token)
@@ -241,23 +241,18 @@ class Palette:
241
241
  for shadenumber in range(-SHADE_COUNT, SHADE_COUNT + 1):
242
242
  if shadenumber > 0:
243
243
  shadeindex = f"+{shadenumber}"
244
- blend_color = white
245
- blend_multiplier = 1
244
+ blended = color.blend(
245
+ white, SHADE_INCREMENT * shadenumber
246
+ )
246
247
 
247
248
  elif shadenumber == 0:
248
249
  shadeindex = ""
249
-
250
- else:
251
- shadeindex = str(shadenumber)
252
- blend_color = black
253
- blend_multiplier = -1
254
-
255
- if shadenumber == 0:
256
250
  blended = color
257
251
 
258
252
  else:
253
+ shadeindex = str(shadenumber)
259
254
  blended = color.blend(
260
- blend_color, blend_multiplier * SHADE_INCREMENT * shadenumber
255
+ black, -SHADE_INCREMENT * shadenumber
261
256
  )
262
257
 
263
258
  data[f"{name}{shadeindex}"] = blended
@@ -7,8 +7,12 @@ from typing import Match
7
7
  from wcwidth import wcswidth
8
8
 
9
9
  RE_LINK = re.compile(r"(?:\x1b\]8;;([^\\]*)\x1b\\([^\\]*?)\x1b\]8;;\x1b\\)")
10
- RE_ANSI_NEW = re.compile(rf"(\x1b\[(.*?)[mH])|{RE_LINK.pattern}|(\x1b_G(.*?)\x1b\\)")
11
- RE_ANSI = re.compile(r"(?:\x1b\[(.*?)[mH])|(?:\x1b\](.*?)\x1b\\)|(?:\x1b_G(.*?)\x1b\\)")
10
+ RE_ANSI_NEW = re.compile(
11
+ rf"(\x1b\[(.*?)[mHJK])|{RE_LINK.pattern}|(\x1b\]8;;\x1b\\)|(\x1b_G(.*?)\x1b\\)"
12
+ )
13
+ RE_ANSI = re.compile(
14
+ r"(?:\x1b\[(.*?)[mHJK])|(?:\x1b\](.*?)\x1b\\)|(?:\x1b_G(.*?)\x1b\\)"
15
+ )
12
16
  RE_MACRO = re.compile(r"(![a-z0-9_\-]+)(?:\(([\w\/\.?\-=:]+)\))?")
13
17
  RE_MARKUP = re.compile(r"((\\*)\[([^\[\]]*)\])")
14
18
  RE_POSITION = re.compile(r"\x1b\[(\d*?)(?:;(\d*))?H")