zettcode-tui 0.4.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.
- zettcode_tui-0.4.0/.gitignore +37 -0
- zettcode_tui-0.4.0/PKG-INFO +20 -0
- zettcode_tui-0.4.0/pyproject.toml +45 -0
- zettcode_tui-0.4.0/src/zettcode/tui/__init__.py +422 -0
- zettcode_tui-0.4.0/src/zettcode/tui/_compat.py +32 -0
- zettcode_tui-0.4.0/src/zettcode/tui/capabilities.py +107 -0
- zettcode_tui-0.4.0/src/zettcode/tui/clipboard.py +91 -0
- zettcode_tui-0.4.0/src/zettcode/tui/console.py +229 -0
- zettcode_tui-0.4.0/src/zettcode/tui/core/__init__.py +63 -0
- zettcode_tui-0.4.0/src/zettcode/tui/core/app.py +658 -0
- zettcode_tui-0.4.0/src/zettcode/tui/core/events.py +134 -0
- zettcode_tui-0.4.0/src/zettcode/tui/core/focus.py +85 -0
- zettcode_tui-0.4.0/src/zettcode/tui/core/geometry.py +212 -0
- zettcode_tui-0.4.0/src/zettcode/tui/core/host.py +66 -0
- zettcode_tui-0.4.0/src/zettcode/tui/core/keymap.py +218 -0
- zettcode_tui-0.4.0/src/zettcode/tui/core/scheduler.py +105 -0
- zettcode_tui-0.4.0/src/zettcode/tui/core/screen.py +80 -0
- zettcode_tui-0.4.0/src/zettcode/tui/core/theme.py +248 -0
- zettcode_tui-0.4.0/src/zettcode/tui/core/widget.py +204 -0
- zettcode_tui-0.4.0/src/zettcode/tui/gallery.py +588 -0
- zettcode_tui-0.4.0/src/zettcode/tui/glyphs.py +73 -0
- zettcode_tui-0.4.0/src/zettcode/tui/input/__init__.py +18 -0
- zettcode_tui-0.4.0/src/zettcode/tui/input/bridge.py +46 -0
- zettcode_tui-0.4.0/src/zettcode/tui/input/decoder.py +234 -0
- zettcode_tui-0.4.0/src/zettcode/tui/input/events.py +64 -0
- zettcode_tui-0.4.0/src/zettcode/tui/input/keys.py +289 -0
- zettcode_tui-0.4.0/src/zettcode/tui/input/reader.py +147 -0
- zettcode_tui-0.4.0/src/zettcode/tui/layout/__init__.py +24 -0
- zettcode_tui-0.4.0/src/zettcode/tui/layout/box.py +153 -0
- zettcode_tui-0.4.0/src/zettcode/tui/layout/overlay.py +164 -0
- zettcode_tui-0.4.0/src/zettcode/tui/layout/scroll.py +477 -0
- zettcode_tui-0.4.0/src/zettcode/tui/layout/solver.py +95 -0
- zettcode_tui-0.4.0/src/zettcode/tui/layout/spacing.py +137 -0
- zettcode_tui-0.4.0/src/zettcode/tui/py.typed +0 -0
- zettcode_tui-0.4.0/src/zettcode/tui/render/__init__.py +71 -0
- zettcode_tui-0.4.0/src/zettcode/tui/render/ansi.py +371 -0
- zettcode_tui-0.4.0/src/zettcode/tui/render/canvas.py +153 -0
- zettcode_tui-0.4.0/src/zettcode/tui/render/code.py +456 -0
- zettcode_tui-0.4.0/src/zettcode/tui/render/color.py +177 -0
- zettcode_tui-0.4.0/src/zettcode/tui/render/renderer.py +151 -0
- zettcode_tui-0.4.0/src/zettcode/tui/render/rich_text.py +80 -0
- zettcode_tui-0.4.0/src/zettcode/tui/render/style.py +121 -0
- zettcode_tui-0.4.0/src/zettcode/tui/render/sweep.py +87 -0
- zettcode_tui-0.4.0/src/zettcode/tui/render/text.py +319 -0
- zettcode_tui-0.4.0/src/zettcode/tui/runner.py +316 -0
- zettcode_tui-0.4.0/src/zettcode/tui/terminal.py +222 -0
- zettcode_tui-0.4.0/src/zettcode/tui/testing/__init__.py +6 -0
- zettcode_tui-0.4.0/src/zettcode/tui/testing/harness.py +220 -0
- zettcode_tui-0.4.0/src/zettcode/tui/testing/snapshot.py +64 -0
- zettcode_tui-0.4.0/src/zettcode/tui/theme_file.py +300 -0
- zettcode_tui-0.4.0/src/zettcode/tui/themes/ayu-mirage.toml +44 -0
- zettcode_tui-0.4.0/src/zettcode/tui/themes/catppuccin-latte.toml +44 -0
- zettcode_tui-0.4.0/src/zettcode/tui/themes/catppuccin-mocha.toml +44 -0
- zettcode_tui-0.4.0/src/zettcode/tui/themes/dracula.toml +44 -0
- zettcode_tui-0.4.0/src/zettcode/tui/themes/github-dimmed.toml +44 -0
- zettcode_tui-0.4.0/src/zettcode/tui/themes/github-light.toml +44 -0
- zettcode_tui-0.4.0/src/zettcode/tui/themes/gruvbox-dark.toml +44 -0
- zettcode_tui-0.4.0/src/zettcode/tui/themes/gruvbox-light.toml +44 -0
- zettcode_tui-0.4.0/src/zettcode/tui/themes/kanagawa.toml +44 -0
- zettcode_tui-0.4.0/src/zettcode/tui/themes/nord.toml +44 -0
- zettcode_tui-0.4.0/src/zettcode/tui/themes/one-dark.toml +44 -0
- zettcode_tui-0.4.0/src/zettcode/tui/themes/rose-pine-dawn.toml +44 -0
- zettcode_tui-0.4.0/src/zettcode/tui/themes/rose-pine.toml +44 -0
- zettcode_tui-0.4.0/src/zettcode/tui/themes/solarized-dark.toml +44 -0
- zettcode_tui-0.4.0/src/zettcode/tui/themes/solarized-light.toml +44 -0
- zettcode_tui-0.4.0/src/zettcode/tui/themes/tokyo-night.toml +44 -0
- zettcode_tui-0.4.0/src/zettcode/tui/widgets/__init__.py +121 -0
- zettcode_tui-0.4.0/src/zettcode/tui/widgets/collapsible.py +116 -0
- zettcode_tui-0.4.0/src/zettcode/tui/widgets/completion.py +211 -0
- zettcode_tui-0.4.0/src/zettcode/tui/widgets/console.py +442 -0
- zettcode_tui-0.4.0/src/zettcode/tui/widgets/dialog.py +165 -0
- zettcode_tui-0.4.0/src/zettcode/tui/widgets/diff.py +236 -0
- zettcode_tui-0.4.0/src/zettcode/tui/widgets/list.py +293 -0
- zettcode_tui-0.4.0/src/zettcode/tui/widgets/list_page.py +121 -0
- zettcode_tui-0.4.0/src/zettcode/tui/widgets/markdown.py +657 -0
- zettcode_tui-0.4.0/src/zettcode/tui/widgets/progress.py +166 -0
- zettcode_tui-0.4.0/src/zettcode/tui/widgets/rich_text.py +84 -0
- zettcode_tui-0.4.0/src/zettcode/tui/widgets/status.py +106 -0
- zettcode_tui-0.4.0/src/zettcode/tui/widgets/table.py +101 -0
- zettcode_tui-0.4.0/src/zettcode/tui/widgets/tasks.py +130 -0
- zettcode_tui-0.4.0/src/zettcode/tui/widgets/text.py +111 -0
- zettcode_tui-0.4.0/src/zettcode/tui/widgets/textarea.py +671 -0
- zettcode_tui-0.4.0/src/zettcode/tui/widgets/toast.py +137 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
**/.venv/
|
|
2
|
+
**/__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
build/
|
|
6
|
+
dist/
|
|
7
|
+
.smoke/
|
|
8
|
+
.coverage
|
|
9
|
+
**/.pytest_cache/
|
|
10
|
+
**/.ruff_cache/
|
|
11
|
+
.zett-tool-output/
|
|
12
|
+
.env
|
|
13
|
+
.DS_Store
|
|
14
|
+
|
|
15
|
+
# viztracer writes its trace here by default.
|
|
16
|
+
/result.json
|
|
17
|
+
|
|
18
|
+
# The docs site: dependencies, build output, and VitePress' cache.
|
|
19
|
+
node_modules/
|
|
20
|
+
docs/.vitepress/dist/
|
|
21
|
+
docs/.vitepress/cache/
|
|
22
|
+
docs/.vitepress/.temp/
|
|
23
|
+
|
|
24
|
+
# The TypeScript client is built from its sources; only dist/ is published.
|
|
25
|
+
clients/typescript/dist/
|
|
26
|
+
|
|
27
|
+
# The gateway's page is built from clients/gateway into the package when a distribution is made (`make gateway-ui`);
|
|
28
|
+
# the build output is not kept in the repository. pyproject's `artifacts` puts it in the wheel all the same.
|
|
29
|
+
packages/zettcode-gateway/src/zettcode/gateway/static/
|
|
30
|
+
|
|
31
|
+
# The palette gallery is generated by `make palettes`: every widget painted in
|
|
32
|
+
# every palette, written by docs/scripts/palette_gallery.py. Regenerate it
|
|
33
|
+
# rather than committing a 2 MB copy that goes stale the moment a colour moves.
|
|
34
|
+
docs/internal/palettes.html
|
|
35
|
+
|
|
36
|
+
# The release order is generated from the pyproject.toml files (`make order`); it is not kept in git.
|
|
37
|
+
packages/order.txt
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: zettcode-tui
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: The widget framework ZettCode's terminal is drawn with, and nothing else
|
|
5
|
+
Project-URL: Repository, https://github.com/Chang-LeHung/zettcode
|
|
6
|
+
Author: Chang-LeHung
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Keywords: terminal,tui,widgets,zettcode
|
|
9
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
16
|
+
Classifier: Typing :: Typed
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Requires-Dist: pyperclip<2,>=1.9
|
|
19
|
+
Requires-Dist: tomli<3,>=2; python_version < '3.11'
|
|
20
|
+
Requires-Dist: wcwidth<1,>=0.2
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "zettcode-tui"
|
|
3
|
+
description = "The widget framework ZettCode's terminal is drawn with, and nothing else"
|
|
4
|
+
requires-python = ">=3.10"
|
|
5
|
+
dynamic = ["version"]
|
|
6
|
+
license = "MIT"
|
|
7
|
+
authors = [{name = "Chang-LeHung"}]
|
|
8
|
+
keywords = ["zettcode", "tui", "terminal", "widgets"]
|
|
9
|
+
classifiers = [
|
|
10
|
+
"Development Status :: 5 - Production/Stable",
|
|
11
|
+
"Programming Language :: Python :: 3.10",
|
|
12
|
+
"Programming Language :: Python :: 3.11",
|
|
13
|
+
"Programming Language :: Python :: 3.12",
|
|
14
|
+
"Programming Language :: Python :: 3.13",
|
|
15
|
+
"Programming Language :: Python :: 3.14",
|
|
16
|
+
"Programming Language :: Python :: 3.15",
|
|
17
|
+
"Typing :: Typed",
|
|
18
|
+
]
|
|
19
|
+
dependencies = [
|
|
20
|
+
"wcwidth>=0.2,<1",
|
|
21
|
+
"pyperclip>=1.9,<2",
|
|
22
|
+
# `tui/_compat.py` reads a theme file; the stdlib only learned TOML in 3.11.
|
|
23
|
+
"tomli>=2,<3; python_version < '3.11'",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[project.urls]
|
|
27
|
+
Repository = "https://github.com/Chang-LeHung/zettcode"
|
|
28
|
+
|
|
29
|
+
[build-system]
|
|
30
|
+
requires = ["hatchling"]
|
|
31
|
+
build-backend = "hatchling.build"
|
|
32
|
+
|
|
33
|
+
[tool.hatch.version]
|
|
34
|
+
# One version for the whole repository: the engine ships the file.
|
|
35
|
+
path = "../zettcode-engine/src/zettcode/version.py"
|
|
36
|
+
|
|
37
|
+
[tool.hatch.build.targets.wheel]
|
|
38
|
+
packages = ["src/zettcode"]
|
|
39
|
+
|
|
40
|
+
[tool.hatch.build.targets.sdist]
|
|
41
|
+
include = ["/src", "/pyproject.toml"]
|
|
42
|
+
|
|
43
|
+
[tool.pytest.ini_options]
|
|
44
|
+
testpaths = ["tests"]
|
|
45
|
+
asyncio_mode = "auto"
|
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
"""ZettCode's internally implemented terminal UI framework."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import importlib
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
#: Where each public name lives; read by :func:`__getattr__` on first use.
|
|
9
|
+
_EXPORTS = {
|
|
10
|
+
"ARROW_DOWN": ".glyphs",
|
|
11
|
+
"ARROW_UP": ".glyphs",
|
|
12
|
+
"Anchor": ".layout",
|
|
13
|
+
"AnyEvent": ".core",
|
|
14
|
+
"AsyncInput": ".input",
|
|
15
|
+
"BAR_EMPTY": ".glyphs",
|
|
16
|
+
"BAR_FULL": ".glyphs",
|
|
17
|
+
"Binding": ".core",
|
|
18
|
+
"Border": ".layout",
|
|
19
|
+
"AnsiBuffer": ".render",
|
|
20
|
+
"Canvas": ".render",
|
|
21
|
+
"Collapsible": ".widgets",
|
|
22
|
+
"ColorDepth": ".render",
|
|
23
|
+
"Column": ".widgets",
|
|
24
|
+
"Command": ".core",
|
|
25
|
+
"CommandRegistry": ".core",
|
|
26
|
+
"Completer": ".widgets",
|
|
27
|
+
"CompletionItem": ".widgets",
|
|
28
|
+
"CompletionPopup": ".widgets",
|
|
29
|
+
"ConsolePanel": ".widgets",
|
|
30
|
+
"Constraints": ".core",
|
|
31
|
+
"DARK": ".core",
|
|
32
|
+
"DONE": ".glyphs",
|
|
33
|
+
"Dialog": ".widgets",
|
|
34
|
+
"DialogAction": ".widgets",
|
|
35
|
+
"DiffLine": ".widgets",
|
|
36
|
+
"DiffSegment": ".widgets",
|
|
37
|
+
"DiffView": ".widgets",
|
|
38
|
+
"DifferentialRenderer": ".render",
|
|
39
|
+
"ELLIPSIS": ".glyphs",
|
|
40
|
+
"EXAMPLE": ".theme_file",
|
|
41
|
+
"EXPANDED": ".glyphs",
|
|
42
|
+
"EdgeInsets": ".core",
|
|
43
|
+
"Event": ".core",
|
|
44
|
+
"EventKind": ".core",
|
|
45
|
+
"EventType": ".input",
|
|
46
|
+
"FAILED": ".glyphs",
|
|
47
|
+
"FocusEvent": ".core",
|
|
48
|
+
"FocusManager": ".core",
|
|
49
|
+
"HBox": ".layout",
|
|
50
|
+
"HEADER": ".glyphs",
|
|
51
|
+
"Host": ".core",
|
|
52
|
+
"InputDecoder": ".input",
|
|
53
|
+
"InputEvent": ".input",
|
|
54
|
+
"KeyEvent": ".core",
|
|
55
|
+
"Keymap": ".core",
|
|
56
|
+
"LIGHT": ".core",
|
|
57
|
+
"LineSource": ".layout",
|
|
58
|
+
"ListItem": ".widgets",
|
|
59
|
+
"ListPage": ".widgets",
|
|
60
|
+
"ListView": ".widgets",
|
|
61
|
+
"MARKER": ".glyphs",
|
|
62
|
+
"Markdown": ".widgets",
|
|
63
|
+
"MarkdownSource": ".widgets",
|
|
64
|
+
"MarkdownView": ".widgets",
|
|
65
|
+
"MouseAction": ".core",
|
|
66
|
+
"MouseEvent": ".core",
|
|
67
|
+
"Overlay": ".layout",
|
|
68
|
+
"OverlaySlot": ".layout",
|
|
69
|
+
"PENDING": ".glyphs",
|
|
70
|
+
"PROMPT": ".glyphs",
|
|
71
|
+
"Padding": ".layout",
|
|
72
|
+
"PasteEvent": ".core",
|
|
73
|
+
"Point": ".core",
|
|
74
|
+
"ProgressBar": ".widgets",
|
|
75
|
+
"RULE": ".glyphs",
|
|
76
|
+
"RUNNING": ".glyphs",
|
|
77
|
+
"Rect": ".core",
|
|
78
|
+
"ResizeEvent": ".core",
|
|
79
|
+
"RichText": ".widgets",
|
|
80
|
+
"Rule": ".widgets",
|
|
81
|
+
"SEPARATOR": ".glyphs",
|
|
82
|
+
"OPTION": ".capabilities",
|
|
83
|
+
"SKIPPED": ".glyphs",
|
|
84
|
+
"SPINNER": ".glyphs",
|
|
85
|
+
"STATUS": ".glyphs",
|
|
86
|
+
"STEERING": ".glyphs",
|
|
87
|
+
"Scheduler": ".core",
|
|
88
|
+
"Screen": ".core",
|
|
89
|
+
"ScreenStack": ".core",
|
|
90
|
+
"ScrollView": ".layout",
|
|
91
|
+
"Size": ".core",
|
|
92
|
+
"Slot": ".layout",
|
|
93
|
+
"Span": ".render",
|
|
94
|
+
"Spinner": ".widgets",
|
|
95
|
+
"StaticLines": ".layout",
|
|
96
|
+
"StatusBar": ".widgets",
|
|
97
|
+
"Style": ".render",
|
|
98
|
+
"Table": ".widgets",
|
|
99
|
+
"TaskPanel": ".widgets",
|
|
100
|
+
"Terminal": ".terminal",
|
|
101
|
+
"TerminalCapabilities": ".capabilities",
|
|
102
|
+
"TerminalRunner": ".runner",
|
|
103
|
+
"Text": ".widgets",
|
|
104
|
+
"TextArea": ".widgets",
|
|
105
|
+
"TextEvent": ".core",
|
|
106
|
+
"TextLine": ".render",
|
|
107
|
+
"Theme": ".core",
|
|
108
|
+
"ThemeFileError": ".theme_file",
|
|
109
|
+
"Toast": ".widgets",
|
|
110
|
+
"ToolTheme": ".core",
|
|
111
|
+
"Track": ".layout",
|
|
112
|
+
"TuiApp": ".core",
|
|
113
|
+
"VBox": ".layout",
|
|
114
|
+
"Widget": ".core",
|
|
115
|
+
"build_unified": ".widgets",
|
|
116
|
+
"centered": ".layout",
|
|
117
|
+
"detect_capabilities": ".capabilities",
|
|
118
|
+
"detect_reduced_motion": ".capabilities",
|
|
119
|
+
"bundled_themes": ".theme_file",
|
|
120
|
+
"file_theme": ".theme_file",
|
|
121
|
+
"ThemeCatalog": ".theme_file",
|
|
122
|
+
"ThemeSource": ".theme_file",
|
|
123
|
+
"RESERVED_THEMES": ".theme_file",
|
|
124
|
+
"THEME_NAME": ".theme_file",
|
|
125
|
+
"event_key": ".core",
|
|
126
|
+
"key_id": ".core",
|
|
127
|
+
"load_theme": ".theme_file",
|
|
128
|
+
"normalize_key": ".core",
|
|
129
|
+
"parse_background": ".terminal",
|
|
130
|
+
"parse_unified": ".widgets",
|
|
131
|
+
"resolve_tracks": ".layout",
|
|
132
|
+
"run_app": ".runner",
|
|
133
|
+
"scheme_named": ".core",
|
|
134
|
+
"theme_from_toml": ".theme_file",
|
|
135
|
+
"theme_named": ".core",
|
|
136
|
+
"theme_names": ".core",
|
|
137
|
+
"title_sequence": ".terminal",
|
|
138
|
+
"walk": ".core",
|
|
139
|
+
"word_diff": ".widgets",
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
if TYPE_CHECKING: # pragma: no cover - for type checkers, not the runtime
|
|
144
|
+
from .capabilities import OPTION, TerminalCapabilities, detect_capabilities, detect_reduced_motion
|
|
145
|
+
from .core import (
|
|
146
|
+
DARK,
|
|
147
|
+
LIGHT,
|
|
148
|
+
AnyEvent,
|
|
149
|
+
Binding,
|
|
150
|
+
Command,
|
|
151
|
+
CommandRegistry,
|
|
152
|
+
Constraints,
|
|
153
|
+
EdgeInsets,
|
|
154
|
+
Event,
|
|
155
|
+
EventKind,
|
|
156
|
+
FocusEvent,
|
|
157
|
+
FocusManager,
|
|
158
|
+
Host,
|
|
159
|
+
KeyEvent,
|
|
160
|
+
Keymap,
|
|
161
|
+
MouseAction,
|
|
162
|
+
MouseEvent,
|
|
163
|
+
PasteEvent,
|
|
164
|
+
Point,
|
|
165
|
+
Rect,
|
|
166
|
+
ResizeEvent,
|
|
167
|
+
Scheduler,
|
|
168
|
+
Screen,
|
|
169
|
+
ScreenStack,
|
|
170
|
+
Size,
|
|
171
|
+
TextEvent,
|
|
172
|
+
Theme,
|
|
173
|
+
ToolTheme,
|
|
174
|
+
TuiApp,
|
|
175
|
+
Widget,
|
|
176
|
+
event_key,
|
|
177
|
+
key_id,
|
|
178
|
+
normalize_key,
|
|
179
|
+
scheme_named,
|
|
180
|
+
theme_named,
|
|
181
|
+
theme_names,
|
|
182
|
+
walk,
|
|
183
|
+
)
|
|
184
|
+
from .glyphs import (
|
|
185
|
+
ARROW_DOWN,
|
|
186
|
+
ARROW_UP,
|
|
187
|
+
BAR_EMPTY,
|
|
188
|
+
BAR_FULL,
|
|
189
|
+
DONE,
|
|
190
|
+
ELLIPSIS,
|
|
191
|
+
EXPANDED,
|
|
192
|
+
FAILED,
|
|
193
|
+
HEADER,
|
|
194
|
+
MARKER,
|
|
195
|
+
PENDING,
|
|
196
|
+
PROMPT,
|
|
197
|
+
RULE,
|
|
198
|
+
RUNNING,
|
|
199
|
+
SEPARATOR,
|
|
200
|
+
SKIPPED,
|
|
201
|
+
SPINNER,
|
|
202
|
+
STATUS,
|
|
203
|
+
STEERING,
|
|
204
|
+
)
|
|
205
|
+
from .input import AsyncInput, EventType, InputDecoder, InputEvent
|
|
206
|
+
from .layout import (
|
|
207
|
+
Anchor,
|
|
208
|
+
Border,
|
|
209
|
+
HBox,
|
|
210
|
+
LineSource,
|
|
211
|
+
Overlay,
|
|
212
|
+
OverlaySlot,
|
|
213
|
+
Padding,
|
|
214
|
+
ScrollView,
|
|
215
|
+
Slot,
|
|
216
|
+
StaticLines,
|
|
217
|
+
Track,
|
|
218
|
+
VBox,
|
|
219
|
+
centered,
|
|
220
|
+
resolve_tracks,
|
|
221
|
+
)
|
|
222
|
+
from .render import AnsiBuffer, Canvas, ColorDepth, DifferentialRenderer, Span, Style, TextLine
|
|
223
|
+
from .runner import TerminalRunner, run_app
|
|
224
|
+
from .terminal import Terminal, parse_background, title_sequence
|
|
225
|
+
from .theme_file import (
|
|
226
|
+
EXAMPLE,
|
|
227
|
+
RESERVED_THEMES,
|
|
228
|
+
THEME_NAME,
|
|
229
|
+
ThemeCatalog,
|
|
230
|
+
ThemeFileError,
|
|
231
|
+
ThemeSource,
|
|
232
|
+
bundled_themes,
|
|
233
|
+
file_theme,
|
|
234
|
+
load_theme,
|
|
235
|
+
theme_from_toml,
|
|
236
|
+
)
|
|
237
|
+
from .widgets import (
|
|
238
|
+
Collapsible,
|
|
239
|
+
Column,
|
|
240
|
+
Completer,
|
|
241
|
+
CompletionItem,
|
|
242
|
+
CompletionPopup,
|
|
243
|
+
ConsolePanel,
|
|
244
|
+
Dialog,
|
|
245
|
+
DialogAction,
|
|
246
|
+
DiffLine,
|
|
247
|
+
DiffSegment,
|
|
248
|
+
DiffView,
|
|
249
|
+
ListItem,
|
|
250
|
+
ListPage,
|
|
251
|
+
ListView,
|
|
252
|
+
Markdown,
|
|
253
|
+
MarkdownSource,
|
|
254
|
+
MarkdownView,
|
|
255
|
+
ProgressBar,
|
|
256
|
+
RichText,
|
|
257
|
+
Rule,
|
|
258
|
+
Spinner,
|
|
259
|
+
StatusBar,
|
|
260
|
+
Table,
|
|
261
|
+
TaskPanel,
|
|
262
|
+
Text,
|
|
263
|
+
TextArea,
|
|
264
|
+
Toast,
|
|
265
|
+
build_unified,
|
|
266
|
+
parse_unified,
|
|
267
|
+
word_diff,
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def __getattr__(name: str) -> object:
|
|
272
|
+
"""Import the module a public name lives in, the first time it is asked for.
|
|
273
|
+
|
|
274
|
+
This package is a facade over several subpackages, and a caller that wants
|
|
275
|
+
one name should not pay for the rest of them — the TUI alone holds the
|
|
276
|
+
widget library, the Markdown parser, the diff viewer, and the syntax
|
|
277
|
+
scanners. ``__all__`` is still the public surface.
|
|
278
|
+
"""
|
|
279
|
+
try:
|
|
280
|
+
module = _EXPORTS[name]
|
|
281
|
+
except KeyError:
|
|
282
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}") from None
|
|
283
|
+
return getattr(importlib.import_module(module, __name__), name)
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def __dir__() -> list[str]:
|
|
287
|
+
"""Return the names this package publishes."""
|
|
288
|
+
return sorted(__all__)
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
__all__ = [
|
|
292
|
+
"AnyEvent",
|
|
293
|
+
"ARROW_DOWN",
|
|
294
|
+
"ARROW_UP",
|
|
295
|
+
"BAR_EMPTY",
|
|
296
|
+
"BAR_FULL",
|
|
297
|
+
"DONE",
|
|
298
|
+
"ELLIPSIS",
|
|
299
|
+
"EXPANDED",
|
|
300
|
+
"FAILED",
|
|
301
|
+
"HEADER",
|
|
302
|
+
"MARKER",
|
|
303
|
+
"OPTION",
|
|
304
|
+
"PENDING",
|
|
305
|
+
"PROMPT",
|
|
306
|
+
"RULE",
|
|
307
|
+
"RUNNING",
|
|
308
|
+
"SEPARATOR",
|
|
309
|
+
"SKIPPED",
|
|
310
|
+
"SPINNER",
|
|
311
|
+
"STATUS",
|
|
312
|
+
"STEERING",
|
|
313
|
+
"Anchor",
|
|
314
|
+
"AsyncInput",
|
|
315
|
+
"TuiApp",
|
|
316
|
+
"Binding",
|
|
317
|
+
"Border",
|
|
318
|
+
"AnsiBuffer",
|
|
319
|
+
"Canvas",
|
|
320
|
+
"Collapsible",
|
|
321
|
+
"ColorDepth",
|
|
322
|
+
"Column",
|
|
323
|
+
"Command",
|
|
324
|
+
"CommandRegistry",
|
|
325
|
+
"Completer",
|
|
326
|
+
"CompletionItem",
|
|
327
|
+
"CompletionPopup",
|
|
328
|
+
"ConsolePanel",
|
|
329
|
+
"Constraints",
|
|
330
|
+
"DARK",
|
|
331
|
+
"DifferentialRenderer",
|
|
332
|
+
"Dialog",
|
|
333
|
+
"DialogAction",
|
|
334
|
+
"DiffLine",
|
|
335
|
+
"DiffSegment",
|
|
336
|
+
"DiffView",
|
|
337
|
+
"EdgeInsets",
|
|
338
|
+
"Event",
|
|
339
|
+
"EventKind",
|
|
340
|
+
"EventType",
|
|
341
|
+
"FocusEvent",
|
|
342
|
+
"FocusManager",
|
|
343
|
+
"HBox",
|
|
344
|
+
"Host",
|
|
345
|
+
"InputDecoder",
|
|
346
|
+
"InputEvent",
|
|
347
|
+
"KeyEvent",
|
|
348
|
+
"Keymap",
|
|
349
|
+
"LIGHT",
|
|
350
|
+
"LineSource",
|
|
351
|
+
"ListItem",
|
|
352
|
+
"ListPage",
|
|
353
|
+
"ListView",
|
|
354
|
+
"Markdown",
|
|
355
|
+
"MarkdownSource",
|
|
356
|
+
"MarkdownView",
|
|
357
|
+
"MouseAction",
|
|
358
|
+
"MouseEvent",
|
|
359
|
+
"Overlay",
|
|
360
|
+
"OverlaySlot",
|
|
361
|
+
"Padding",
|
|
362
|
+
"PasteEvent",
|
|
363
|
+
"Point",
|
|
364
|
+
"ProgressBar",
|
|
365
|
+
"RichText",
|
|
366
|
+
"Rect",
|
|
367
|
+
"ResizeEvent",
|
|
368
|
+
"Rule",
|
|
369
|
+
"Scheduler",
|
|
370
|
+
"Screen",
|
|
371
|
+
"ScreenStack",
|
|
372
|
+
"ScrollView",
|
|
373
|
+
"Size",
|
|
374
|
+
"Slot",
|
|
375
|
+
"Span",
|
|
376
|
+
"Spinner",
|
|
377
|
+
"StaticLines",
|
|
378
|
+
"StatusBar",
|
|
379
|
+
"Style",
|
|
380
|
+
"Table",
|
|
381
|
+
"TaskPanel",
|
|
382
|
+
"Terminal",
|
|
383
|
+
"TerminalRunner",
|
|
384
|
+
"TerminalCapabilities",
|
|
385
|
+
"Text",
|
|
386
|
+
"TextArea",
|
|
387
|
+
"TextEvent",
|
|
388
|
+
"TextLine",
|
|
389
|
+
"Theme",
|
|
390
|
+
"ThemeFileError",
|
|
391
|
+
"Toast",
|
|
392
|
+
"Track",
|
|
393
|
+
"VBox",
|
|
394
|
+
"Widget",
|
|
395
|
+
"build_unified",
|
|
396
|
+
"EXAMPLE",
|
|
397
|
+
"detect_capabilities",
|
|
398
|
+
"bundled_themes",
|
|
399
|
+
"file_theme",
|
|
400
|
+
"RESERVED_THEMES",
|
|
401
|
+
"THEME_NAME",
|
|
402
|
+
"ThemeCatalog",
|
|
403
|
+
"ThemeSource",
|
|
404
|
+
"detect_reduced_motion",
|
|
405
|
+
"centered",
|
|
406
|
+
"event_key",
|
|
407
|
+
"key_id",
|
|
408
|
+
"load_theme",
|
|
409
|
+
"normalize_key",
|
|
410
|
+
"parse_unified",
|
|
411
|
+
"resolve_tracks",
|
|
412
|
+
"run_app",
|
|
413
|
+
"theme_named",
|
|
414
|
+
"title_sequence",
|
|
415
|
+
"scheme_named",
|
|
416
|
+
"parse_background",
|
|
417
|
+
"ToolTheme",
|
|
418
|
+
"theme_names",
|
|
419
|
+
"theme_from_toml",
|
|
420
|
+
"walk",
|
|
421
|
+
"word_diff",
|
|
422
|
+
]
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""The two version shims the framework needs, so `zettcode-tui` depends on nothing of ours.
|
|
2
|
+
|
|
3
|
+
The release notes of the framework say what it is: a widget toolkit that knows nothing of the agent. It needs the
|
|
4
|
+
string-valued enum (the standard library's is 3.11's, and its spelling is what the rows and the theme files rely on) and
|
|
5
|
+
a TOML reader for a theme file. Both are ten lines here rather than an import of another distribution, which would put
|
|
6
|
+
`zett-agent` behind a widget.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import sys
|
|
12
|
+
from enum import Enum
|
|
13
|
+
|
|
14
|
+
if sys.version_info >= (3, 11):
|
|
15
|
+
import tomllib
|
|
16
|
+
else: # pragma: no cover - the branch only the 3.10 runtime takes
|
|
17
|
+
import tomli as tomllib
|
|
18
|
+
|
|
19
|
+
__all__ = ["StrEnum", "tomllib"]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class StrEnum(str, Enum):
|
|
23
|
+
"""A string-valued enum, for the versions before ``enum.StrEnum``.
|
|
24
|
+
|
|
25
|
+
The standard library's version formats as its value, so ``f"{member}"`` is ``"ready"`` rather than
|
|
26
|
+
``"Activity.READY"``; the rows and the theme files rely on that, so every interpreter uses this class instead of the
|
|
27
|
+
standard one and the wording cannot drift with the version.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __str__(self) -> str:
|
|
31
|
+
"""Return the member's value, as ``enum.StrEnum`` does."""
|
|
32
|
+
return str(self.value)
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"""Terminal capability detection used to pick conservative rendering options."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
from collections.abc import Mapping
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
|
|
10
|
+
from .render.color import ColorDepth
|
|
11
|
+
|
|
12
|
+
#: What the modifier that sends ``alt``-prefixed keys is called on this machine: the Option key on a Mac, Alt
|
|
13
|
+
#: elsewhere. The same physical key sends the same bytes; only its name differs, and a hint that says "alt" to
|
|
14
|
+
#: someone looking at a key marked "option" is a hint they have to translate.
|
|
15
|
+
OPTION = "option" if sys.platform == "darwin" else "alt"
|
|
16
|
+
|
|
17
|
+
_TRUECOLOR_COLORTERM = {"truecolor", "24bit"}
|
|
18
|
+
_MOTION_VARIABLES = ("ZETTCODE_REDUCED_MOTION", "NO_MOTION", "REDUCED_MOTION")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass(frozen=True, slots=True)
|
|
22
|
+
class TerminalCapabilities:
|
|
23
|
+
"""What the attached terminal can be trusted to render.
|
|
24
|
+
|
|
25
|
+
Attributes:
|
|
26
|
+
color_depth: Safest depth the environment advertises.
|
|
27
|
+
unicode: The locale looks like it can encode UTF-8. Advisory for now: no
|
|
28
|
+
widget switches to an ASCII glyph set when it is False.
|
|
29
|
+
mouse: The terminal accepts mouse reporting. Advisory, like ``unicode``.
|
|
30
|
+
bracketed_paste: The terminal wraps pastes in ``ESC[200~`` guards.
|
|
31
|
+
Advisory as well; the decoder accepts them either way.
|
|
32
|
+
width: Initial width guess in cells, replaced on the first resize.
|
|
33
|
+
height: Initial height guess in cells.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
color_depth: ColorDepth = ColorDepth.ANSI16
|
|
37
|
+
unicode: bool = True
|
|
38
|
+
mouse: bool = True
|
|
39
|
+
bracketed_paste: bool = True
|
|
40
|
+
width: int = 80
|
|
41
|
+
height: int = 24
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def truecolor(self) -> bool:
|
|
45
|
+
"""Return whether the terminal can be trusted with 24-bit colour."""
|
|
46
|
+
return self.color_depth is ColorDepth.TRUECOLOR
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def detect_capabilities(environ: Mapping[str, str] | None = None) -> TerminalCapabilities:
|
|
50
|
+
"""Derive capabilities from environment variables.
|
|
51
|
+
|
|
52
|
+
Everything here is a heuristic, so each default errs toward the option that
|
|
53
|
+
still looks correct on a weaker terminal.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
environ: Environment to read; defaults to ``os.environ`` and is
|
|
57
|
+
injectable so tests can describe a terminal without owning one.
|
|
58
|
+
"""
|
|
59
|
+
env = os.environ if environ is None else environ
|
|
60
|
+
return TerminalCapabilities(
|
|
61
|
+
color_depth=detect_color_depth(env),
|
|
62
|
+
unicode=detect_unicode(env),
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def detect_color_depth(environ: Mapping[str, str] | None = None) -> ColorDepth:
|
|
67
|
+
"""Return the safest color depth the environment advertises.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
environ: Environment to read; defaults to ``os.environ``.
|
|
71
|
+
"""
|
|
72
|
+
env = os.environ if environ is None else environ
|
|
73
|
+
term = env.get("TERM", "")
|
|
74
|
+
if env.get("NO_COLOR") or term == "dumb":
|
|
75
|
+
return ColorDepth.MONO
|
|
76
|
+
colorterm = env.get("COLORTERM", "").lower()
|
|
77
|
+
if colorterm in _TRUECOLOR_COLORTERM or "truecolor" in term or "direct" in term:
|
|
78
|
+
return ColorDepth.TRUECOLOR
|
|
79
|
+
if "256" in term or colorterm:
|
|
80
|
+
return ColorDepth.ANSI256
|
|
81
|
+
return ColorDepth.ANSI16
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def detect_unicode(environ: Mapping[str, str] | None = None) -> bool:
|
|
85
|
+
"""Return whether the environment looks like it can encode UTF-8.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
environ: Environment to read; defaults to ``os.environ``. An unset
|
|
89
|
+
locale is treated as UTF-8 capable, because that is the common case
|
|
90
|
+
on modern systems.
|
|
91
|
+
"""
|
|
92
|
+
env = os.environ if environ is None else environ
|
|
93
|
+
locale = env.get("LC_ALL") or env.get("LC_CTYPE") or env.get("LANG", "")
|
|
94
|
+
if locale:
|
|
95
|
+
return "utf" in locale.lower()
|
|
96
|
+
return True
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def detect_reduced_motion(environ: Mapping[str, str] | None = None) -> bool:
|
|
100
|
+
"""Return whether decorative animation should be suppressed.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
environ: Environment to read; defaults to ``os.environ``. Any of
|
|
104
|
+
``_MOTION_VARIABLES`` being set to a non-empty value wins.
|
|
105
|
+
"""
|
|
106
|
+
env = os.environ if environ is None else environ
|
|
107
|
+
return any(env.get(name) for name in _MOTION_VARIABLES)
|