zettcode-tui 0.4.0__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.
- zettcode/tui/__init__.py +422 -0
- zettcode/tui/_compat.py +32 -0
- zettcode/tui/capabilities.py +107 -0
- zettcode/tui/clipboard.py +91 -0
- zettcode/tui/console.py +229 -0
- zettcode/tui/core/__init__.py +63 -0
- zettcode/tui/core/app.py +658 -0
- zettcode/tui/core/events.py +134 -0
- zettcode/tui/core/focus.py +85 -0
- zettcode/tui/core/geometry.py +212 -0
- zettcode/tui/core/host.py +66 -0
- zettcode/tui/core/keymap.py +218 -0
- zettcode/tui/core/scheduler.py +105 -0
- zettcode/tui/core/screen.py +80 -0
- zettcode/tui/core/theme.py +248 -0
- zettcode/tui/core/widget.py +204 -0
- zettcode/tui/gallery.py +588 -0
- zettcode/tui/glyphs.py +73 -0
- zettcode/tui/input/__init__.py +18 -0
- zettcode/tui/input/bridge.py +46 -0
- zettcode/tui/input/decoder.py +234 -0
- zettcode/tui/input/events.py +64 -0
- zettcode/tui/input/keys.py +289 -0
- zettcode/tui/input/reader.py +147 -0
- zettcode/tui/layout/__init__.py +24 -0
- zettcode/tui/layout/box.py +153 -0
- zettcode/tui/layout/overlay.py +164 -0
- zettcode/tui/layout/scroll.py +477 -0
- zettcode/tui/layout/solver.py +95 -0
- zettcode/tui/layout/spacing.py +137 -0
- zettcode/tui/py.typed +0 -0
- zettcode/tui/render/__init__.py +71 -0
- zettcode/tui/render/ansi.py +371 -0
- zettcode/tui/render/canvas.py +153 -0
- zettcode/tui/render/code.py +456 -0
- zettcode/tui/render/color.py +177 -0
- zettcode/tui/render/renderer.py +151 -0
- zettcode/tui/render/rich_text.py +80 -0
- zettcode/tui/render/style.py +121 -0
- zettcode/tui/render/sweep.py +87 -0
- zettcode/tui/render/text.py +319 -0
- zettcode/tui/runner.py +316 -0
- zettcode/tui/terminal.py +222 -0
- zettcode/tui/testing/__init__.py +6 -0
- zettcode/tui/testing/harness.py +220 -0
- zettcode/tui/testing/snapshot.py +64 -0
- zettcode/tui/theme_file.py +300 -0
- zettcode/tui/themes/ayu-mirage.toml +44 -0
- zettcode/tui/themes/catppuccin-latte.toml +44 -0
- zettcode/tui/themes/catppuccin-mocha.toml +44 -0
- zettcode/tui/themes/dracula.toml +44 -0
- zettcode/tui/themes/github-dimmed.toml +44 -0
- zettcode/tui/themes/github-light.toml +44 -0
- zettcode/tui/themes/gruvbox-dark.toml +44 -0
- zettcode/tui/themes/gruvbox-light.toml +44 -0
- zettcode/tui/themes/kanagawa.toml +44 -0
- zettcode/tui/themes/nord.toml +44 -0
- zettcode/tui/themes/one-dark.toml +44 -0
- zettcode/tui/themes/rose-pine-dawn.toml +44 -0
- zettcode/tui/themes/rose-pine.toml +44 -0
- zettcode/tui/themes/solarized-dark.toml +44 -0
- zettcode/tui/themes/solarized-light.toml +44 -0
- zettcode/tui/themes/tokyo-night.toml +44 -0
- zettcode/tui/widgets/__init__.py +121 -0
- zettcode/tui/widgets/collapsible.py +116 -0
- zettcode/tui/widgets/completion.py +211 -0
- zettcode/tui/widgets/console.py +442 -0
- zettcode/tui/widgets/dialog.py +165 -0
- zettcode/tui/widgets/diff.py +236 -0
- zettcode/tui/widgets/list.py +293 -0
- zettcode/tui/widgets/list_page.py +121 -0
- zettcode/tui/widgets/markdown.py +657 -0
- zettcode/tui/widgets/progress.py +166 -0
- zettcode/tui/widgets/rich_text.py +84 -0
- zettcode/tui/widgets/status.py +106 -0
- zettcode/tui/widgets/table.py +101 -0
- zettcode/tui/widgets/tasks.py +130 -0
- zettcode/tui/widgets/text.py +111 -0
- zettcode/tui/widgets/textarea.py +671 -0
- zettcode/tui/widgets/toast.py +137 -0
- zettcode_tui-0.4.0.dist-info/METADATA +20 -0
- zettcode_tui-0.4.0.dist-info/RECORD +83 -0
- zettcode_tui-0.4.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
description = "ink blue and faded gold"
|
|
2
|
+
base = "dark"
|
|
3
|
+
|
|
4
|
+
[ui]
|
|
5
|
+
background = "#1f1f28"
|
|
6
|
+
surface = "#252535"
|
|
7
|
+
surface_alt = "#2a2a37"
|
|
8
|
+
surface_side = "#323547"
|
|
9
|
+
surface_peer = "#383548"
|
|
10
|
+
text = "#dcd7ba"
|
|
11
|
+
muted = "#8b8a83"
|
|
12
|
+
subtle = "#c8c093"
|
|
13
|
+
accent = "#7e9cd8"
|
|
14
|
+
accent_bright = "#7fb4ca"
|
|
15
|
+
warning = "#e6c384"
|
|
16
|
+
error = "#e46876"
|
|
17
|
+
border = "#363646"
|
|
18
|
+
focus = "#7fb4ca"
|
|
19
|
+
selection = "#2d4f67"
|
|
20
|
+
diff_added = "#98bb6c"
|
|
21
|
+
diff_removed = "#e46876"
|
|
22
|
+
|
|
23
|
+
[tools]
|
|
24
|
+
read = "#e6c384"
|
|
25
|
+
search = "#7e9cd8"
|
|
26
|
+
image = "#7e9cd8"
|
|
27
|
+
write = "#ffa066"
|
|
28
|
+
delete = "#e46876"
|
|
29
|
+
shell = "#7e9cd8"
|
|
30
|
+
plan = "#7e9cd8"
|
|
31
|
+
subagent = "#7aa89f"
|
|
32
|
+
message = "#d27e99"
|
|
33
|
+
|
|
34
|
+
[code]
|
|
35
|
+
inline = "#98bb6c"
|
|
36
|
+
text = "#dcd7ba"
|
|
37
|
+
keyword = "#957fb8"
|
|
38
|
+
string = "#98bb6c"
|
|
39
|
+
comment = "#8b8a83"
|
|
40
|
+
number = "#ffa066"
|
|
41
|
+
function = "#7e9cd8"
|
|
42
|
+
builtin = "#957fb8"
|
|
43
|
+
operator = "#c8c093"
|
|
44
|
+
punctuator = "#c8c093"
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
description = "arctic blue-grey, very low saturation"
|
|
2
|
+
base = "dark"
|
|
3
|
+
|
|
4
|
+
[ui]
|
|
5
|
+
background = "#2e3440"
|
|
6
|
+
surface = "#343b49"
|
|
7
|
+
surface_alt = "#3b4252"
|
|
8
|
+
surface_side = "#424c5d"
|
|
9
|
+
surface_peer = "#4b4c5e"
|
|
10
|
+
text = "#eceff4"
|
|
11
|
+
muted = "#9ca6b8"
|
|
12
|
+
subtle = "#d8dee9"
|
|
13
|
+
accent = "#88c0d0"
|
|
14
|
+
accent_bright = "#8fbcbb"
|
|
15
|
+
warning = "#ebcb8b"
|
|
16
|
+
error = "#ca7d84"
|
|
17
|
+
border = "#4c566a"
|
|
18
|
+
focus = "#8fbcbb"
|
|
19
|
+
selection = "#434c5e"
|
|
20
|
+
diff_added = "#a3be8c"
|
|
21
|
+
diff_removed = "#ca7d84"
|
|
22
|
+
|
|
23
|
+
[tools]
|
|
24
|
+
read = "#ebcb8b"
|
|
25
|
+
search = "#81a1c1"
|
|
26
|
+
image = "#81a1c1"
|
|
27
|
+
write = "#d08770"
|
|
28
|
+
delete = "#ca7d84"
|
|
29
|
+
shell = "#81a1c1"
|
|
30
|
+
plan = "#81a1c1"
|
|
31
|
+
subagent = "#8fbcbb"
|
|
32
|
+
message = "#b48ead"
|
|
33
|
+
|
|
34
|
+
[code]
|
|
35
|
+
inline = "#a3be8c"
|
|
36
|
+
text = "#eceff4"
|
|
37
|
+
keyword = "#81a1c1"
|
|
38
|
+
string = "#a3be8c"
|
|
39
|
+
comment = "#9ca6b8"
|
|
40
|
+
number = "#d08770"
|
|
41
|
+
function = "#81a1c1"
|
|
42
|
+
builtin = "#b48ead"
|
|
43
|
+
operator = "#d8dee9"
|
|
44
|
+
punctuator = "#d8dee9"
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
description = "Atom's neutral blue-grey classic"
|
|
2
|
+
base = "dark"
|
|
3
|
+
|
|
4
|
+
[ui]
|
|
5
|
+
background = "#282c34"
|
|
6
|
+
surface = "#2c313a"
|
|
7
|
+
surface_alt = "#333842"
|
|
8
|
+
surface_side = "#384453"
|
|
9
|
+
surface_peer = "#464056"
|
|
10
|
+
text = "#c4c9d2"
|
|
11
|
+
muted = "#959aa2"
|
|
12
|
+
subtle = "#acb2bf"
|
|
13
|
+
accent = "#61afef"
|
|
14
|
+
accent_bright = "#56b6c2"
|
|
15
|
+
warning = "#e5c07b"
|
|
16
|
+
error = "#e06c75"
|
|
17
|
+
border = "#3e4451"
|
|
18
|
+
focus = "#56b6c2"
|
|
19
|
+
selection = "#3e4451"
|
|
20
|
+
diff_added = "#98c379"
|
|
21
|
+
diff_removed = "#e06c75"
|
|
22
|
+
|
|
23
|
+
[tools]
|
|
24
|
+
read = "#e5c07b"
|
|
25
|
+
search = "#61afef"
|
|
26
|
+
image = "#61afef"
|
|
27
|
+
write = "#d19a66"
|
|
28
|
+
delete = "#e06c75"
|
|
29
|
+
shell = "#61afef"
|
|
30
|
+
plan = "#61afef"
|
|
31
|
+
subagent = "#56b6c2"
|
|
32
|
+
message = "#c678dd"
|
|
33
|
+
|
|
34
|
+
[code]
|
|
35
|
+
inline = "#98c379"
|
|
36
|
+
text = "#c4c9d2"
|
|
37
|
+
keyword = "#c678dd"
|
|
38
|
+
string = "#98c379"
|
|
39
|
+
comment = "#959aa2"
|
|
40
|
+
number = "#d19a66"
|
|
41
|
+
function = "#61afef"
|
|
42
|
+
builtin = "#c678dd"
|
|
43
|
+
operator = "#acb2bf"
|
|
44
|
+
punctuator = "#acb2bf"
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
description = "warm paper with violet and rose"
|
|
2
|
+
base = "light"
|
|
3
|
+
|
|
4
|
+
[ui]
|
|
5
|
+
background = "#faf4ed"
|
|
6
|
+
surface = "#fffaf3"
|
|
7
|
+
surface_alt = "#f2e9e1"
|
|
8
|
+
surface_side = "#dedcd8"
|
|
9
|
+
surface_peer = "#e5dbda"
|
|
10
|
+
text = "#4c486a"
|
|
11
|
+
muted = "#726e7c"
|
|
12
|
+
subtle = "#5b586e"
|
|
13
|
+
accent = "#826e98"
|
|
14
|
+
accent_bright = "#a76562"
|
|
15
|
+
warning = "#9e6a23"
|
|
16
|
+
error = "#ab5e74"
|
|
17
|
+
border = "#dfdad9"
|
|
18
|
+
focus = "#a76562"
|
|
19
|
+
selection = "#dfdad9"
|
|
20
|
+
diff_added = "#497e87"
|
|
21
|
+
diff_removed = "#ab5e74"
|
|
22
|
+
|
|
23
|
+
[tools]
|
|
24
|
+
read = "#9a6e3c"
|
|
25
|
+
search = "#497e87"
|
|
26
|
+
image = "#497e87"
|
|
27
|
+
write = "#9e6a23"
|
|
28
|
+
delete = "#ab5e74"
|
|
29
|
+
shell = "#497e87"
|
|
30
|
+
plan = "#497e87"
|
|
31
|
+
subagent = "#497e87"
|
|
32
|
+
message = "#ab5e74"
|
|
33
|
+
|
|
34
|
+
[code]
|
|
35
|
+
inline = "#497e87"
|
|
36
|
+
text = "#4c486a"
|
|
37
|
+
keyword = "#286983"
|
|
38
|
+
string = "#497e87"
|
|
39
|
+
comment = "#726e7c"
|
|
40
|
+
number = "#a76562"
|
|
41
|
+
function = "#286983"
|
|
42
|
+
builtin = "#826e98"
|
|
43
|
+
operator = "#5b586e"
|
|
44
|
+
punctuator = "#5b586e"
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
description = "dark violet with rose gold"
|
|
2
|
+
base = "dark"
|
|
3
|
+
|
|
4
|
+
[ui]
|
|
5
|
+
background = "#191724"
|
|
6
|
+
surface = "#1f1d2e"
|
|
7
|
+
surface_alt = "#26233a"
|
|
8
|
+
surface_side = "#272b42"
|
|
9
|
+
surface_peer = "#3b3450"
|
|
10
|
+
text = "#e0def4"
|
|
11
|
+
muted = "#87849b"
|
|
12
|
+
subtle = "#9e9ab5"
|
|
13
|
+
accent = "#c4a7e7"
|
|
14
|
+
accent_bright = "#ebbcba"
|
|
15
|
+
warning = "#f6c177"
|
|
16
|
+
error = "#eb6f92"
|
|
17
|
+
border = "#403d52"
|
|
18
|
+
focus = "#ebbcba"
|
|
19
|
+
selection = "#403d52"
|
|
20
|
+
diff_added = "#9ccfd8"
|
|
21
|
+
diff_removed = "#eb6f92"
|
|
22
|
+
|
|
23
|
+
[tools]
|
|
24
|
+
read = "#f6c177"
|
|
25
|
+
search = "#9ccfd8"
|
|
26
|
+
image = "#9ccfd8"
|
|
27
|
+
write = "#ebbcba"
|
|
28
|
+
delete = "#eb6f92"
|
|
29
|
+
shell = "#9ccfd8"
|
|
30
|
+
plan = "#9ccfd8"
|
|
31
|
+
subagent = "#9ccfd8"
|
|
32
|
+
message = "#eb6f92"
|
|
33
|
+
|
|
34
|
+
[code]
|
|
35
|
+
inline = "#9ccfd8"
|
|
36
|
+
text = "#e0def4"
|
|
37
|
+
keyword = "#46829a"
|
|
38
|
+
string = "#9ccfd8"
|
|
39
|
+
comment = "#87849b"
|
|
40
|
+
number = "#ebbcba"
|
|
41
|
+
function = "#46829a"
|
|
42
|
+
builtin = "#c4a7e7"
|
|
43
|
+
operator = "#9e9ab5"
|
|
44
|
+
punctuator = "#9e9ab5"
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
description = "deep teal, low contrast"
|
|
2
|
+
base = "dark"
|
|
3
|
+
|
|
4
|
+
[ui]
|
|
5
|
+
background = "#002b36"
|
|
6
|
+
surface = "#073642"
|
|
7
|
+
surface_alt = "#0b3d4b"
|
|
8
|
+
surface_side = "#0e4558"
|
|
9
|
+
surface_peer = "#18445b"
|
|
10
|
+
text = "#c4cbcb"
|
|
11
|
+
muted = "#8c9ca2"
|
|
12
|
+
subtle = "#a8b4b6"
|
|
13
|
+
accent = "#268bd2"
|
|
14
|
+
accent_bright = "#2aa198"
|
|
15
|
+
warning = "#b58900"
|
|
16
|
+
error = "#e25653"
|
|
17
|
+
border = "#14505f"
|
|
18
|
+
focus = "#2aa198"
|
|
19
|
+
selection = "#0f4c5c"
|
|
20
|
+
diff_added = "#859900"
|
|
21
|
+
diff_removed = "#e25653"
|
|
22
|
+
|
|
23
|
+
[tools]
|
|
24
|
+
read = "#b58900"
|
|
25
|
+
search = "#268bd2"
|
|
26
|
+
image = "#268bd2"
|
|
27
|
+
write = "#d36639"
|
|
28
|
+
delete = "#e25653"
|
|
29
|
+
shell = "#268bd2"
|
|
30
|
+
plan = "#268bd2"
|
|
31
|
+
subagent = "#2aa198"
|
|
32
|
+
message = "#da5495"
|
|
33
|
+
|
|
34
|
+
[code]
|
|
35
|
+
inline = "#859900"
|
|
36
|
+
text = "#c4cbcb"
|
|
37
|
+
keyword = "#859900"
|
|
38
|
+
string = "#859900"
|
|
39
|
+
comment = "#8c9ca2"
|
|
40
|
+
number = "#d36639"
|
|
41
|
+
function = "#268bd2"
|
|
42
|
+
builtin = "#7b7fca"
|
|
43
|
+
operator = "#a8b4b6"
|
|
44
|
+
punctuator = "#a8b4b6"
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
description = "beige with teal-blue accents"
|
|
2
|
+
base = "light"
|
|
3
|
+
|
|
4
|
+
[ui]
|
|
5
|
+
background = "#fdf6e3"
|
|
6
|
+
surface = "#fffbf0"
|
|
7
|
+
surface_alt = "#eee8d5"
|
|
8
|
+
surface_side = "#dadfd5"
|
|
9
|
+
surface_peer = "#ddd9d3"
|
|
10
|
+
text = "#3e4d52"
|
|
11
|
+
muted = "#677171"
|
|
12
|
+
subtle = "#4c5c62"
|
|
13
|
+
accent = "#227dbd"
|
|
14
|
+
accent_bright = "#23857d"
|
|
15
|
+
warning = "#957100"
|
|
16
|
+
error = "#dc322f"
|
|
17
|
+
border = "#ddd6c1"
|
|
18
|
+
focus = "#23857d"
|
|
19
|
+
selection = "#e4ddc8"
|
|
20
|
+
diff_added = "#6e7e00"
|
|
21
|
+
diff_removed = "#dc322f"
|
|
22
|
+
|
|
23
|
+
[tools]
|
|
24
|
+
read = "#957100"
|
|
25
|
+
search = "#227dbd"
|
|
26
|
+
image = "#227dbd"
|
|
27
|
+
write = "#cb4b16"
|
|
28
|
+
delete = "#dc322f"
|
|
29
|
+
shell = "#227dbd"
|
|
30
|
+
plan = "#227dbd"
|
|
31
|
+
subagent = "#23857d"
|
|
32
|
+
message = "#d33682"
|
|
33
|
+
|
|
34
|
+
[code]
|
|
35
|
+
inline = "#6e7e00"
|
|
36
|
+
text = "#3e4d52"
|
|
37
|
+
keyword = "#6e7e00"
|
|
38
|
+
string = "#6e7e00"
|
|
39
|
+
comment = "#677171"
|
|
40
|
+
number = "#cb4b16"
|
|
41
|
+
function = "#227dbd"
|
|
42
|
+
builtin = "#6c71c4"
|
|
43
|
+
operator = "#4c5c62"
|
|
44
|
+
punctuator = "#4c5c62"
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
description = "deep indigo, cool neon blues"
|
|
2
|
+
base = "dark"
|
|
3
|
+
|
|
4
|
+
[ui]
|
|
5
|
+
background = "#1a1b26"
|
|
6
|
+
surface = "#1f2335"
|
|
7
|
+
surface_alt = "#292e42"
|
|
8
|
+
surface_side = "#313a54"
|
|
9
|
+
surface_peer = "#3c3c5a"
|
|
10
|
+
text = "#c0caf5"
|
|
11
|
+
muted = "#858cab"
|
|
12
|
+
subtle = "#a9b1d6"
|
|
13
|
+
accent = "#7aa2f7"
|
|
14
|
+
accent_bright = "#7dcfff"
|
|
15
|
+
warning = "#e0af68"
|
|
16
|
+
error = "#f7768e"
|
|
17
|
+
border = "#3b4261"
|
|
18
|
+
focus = "#7dcfff"
|
|
19
|
+
selection = "#2f3b66"
|
|
20
|
+
diff_added = "#9ece6a"
|
|
21
|
+
diff_removed = "#f7768e"
|
|
22
|
+
|
|
23
|
+
[tools]
|
|
24
|
+
read = "#e0af68"
|
|
25
|
+
search = "#7aa2f7"
|
|
26
|
+
image = "#7aa2f7"
|
|
27
|
+
write = "#ff9e64"
|
|
28
|
+
delete = "#f7768e"
|
|
29
|
+
shell = "#7aa2f7"
|
|
30
|
+
plan = "#7aa2f7"
|
|
31
|
+
subagent = "#73daca"
|
|
32
|
+
message = "#bb9af7"
|
|
33
|
+
|
|
34
|
+
[code]
|
|
35
|
+
inline = "#9ece6a"
|
|
36
|
+
text = "#c0caf5"
|
|
37
|
+
keyword = "#bb9af7"
|
|
38
|
+
string = "#9ece6a"
|
|
39
|
+
comment = "#858cab"
|
|
40
|
+
number = "#ff9e64"
|
|
41
|
+
function = "#7aa2f7"
|
|
42
|
+
builtin = "#bb9af7"
|
|
43
|
+
operator = "#a9b1d6"
|
|
44
|
+
punctuator = "#a9b1d6"
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""Reusable widgets built on the core runtime and the layout containers."""
|
|
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
|
+
"Collapsible": ".collapsible",
|
|
11
|
+
"Column": ".table",
|
|
12
|
+
"Completer": ".completion",
|
|
13
|
+
"CompletionItem": ".completion",
|
|
14
|
+
"CompletionPopup": ".completion",
|
|
15
|
+
"ConsolePanel": ".console",
|
|
16
|
+
"Dialog": ".dialog",
|
|
17
|
+
"DialogAction": ".dialog",
|
|
18
|
+
"DiffLine": ".diff",
|
|
19
|
+
"DiffSegment": ".diff",
|
|
20
|
+
"DiffView": ".diff",
|
|
21
|
+
"ListItem": ".list",
|
|
22
|
+
"ListPage": ".list_page",
|
|
23
|
+
"ListView": ".list",
|
|
24
|
+
"Markdown": ".markdown",
|
|
25
|
+
"MarkdownSource": ".markdown",
|
|
26
|
+
"MarkdownView": ".markdown",
|
|
27
|
+
"ProgressBar": ".progress",
|
|
28
|
+
"RichText": ".rich_text",
|
|
29
|
+
"Rule": ".text",
|
|
30
|
+
"Spinner": ".progress",
|
|
31
|
+
"StatusBar": ".status",
|
|
32
|
+
"Table": ".table",
|
|
33
|
+
"TaskPanel": ".tasks",
|
|
34
|
+
"Text": ".text",
|
|
35
|
+
"TextArea": ".textarea",
|
|
36
|
+
"Toast": ".toast",
|
|
37
|
+
"build_unified": ".diff",
|
|
38
|
+
"layout_input": ".textarea",
|
|
39
|
+
"next_word": ".textarea",
|
|
40
|
+
"parse_unified": ".diff",
|
|
41
|
+
"previous_word": ".textarea",
|
|
42
|
+
"render_markdown": ".markdown",
|
|
43
|
+
"word_diff": ".diff",
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
if TYPE_CHECKING: # pragma: no cover - for type checkers, not the runtime
|
|
48
|
+
from .collapsible import Collapsible
|
|
49
|
+
from .completion import Completer, CompletionItem, CompletionPopup
|
|
50
|
+
from .console import ConsolePanel
|
|
51
|
+
from .dialog import Dialog, DialogAction
|
|
52
|
+
from .diff import DiffLine, DiffSegment, DiffView, build_unified, parse_unified, word_diff
|
|
53
|
+
from .list import ListItem, ListView
|
|
54
|
+
from .list_page import ListPage
|
|
55
|
+
from .markdown import Markdown, MarkdownSource, MarkdownView, render_markdown
|
|
56
|
+
from .progress import ProgressBar, Spinner
|
|
57
|
+
from .rich_text import RichText
|
|
58
|
+
from .status import StatusBar
|
|
59
|
+
from .table import Column, Table
|
|
60
|
+
from .tasks import TaskPanel
|
|
61
|
+
from .text import Rule, Text
|
|
62
|
+
from .textarea import TextArea, layout_input, next_word, previous_word
|
|
63
|
+
from .toast import Toast
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def __getattr__(name: str) -> object:
|
|
67
|
+
"""Import the module a public name lives in, the first time it is asked for.
|
|
68
|
+
|
|
69
|
+
This package is a facade over several subpackages, and a caller that wants
|
|
70
|
+
one name should not pay for the rest of them — the TUI alone holds the
|
|
71
|
+
widget library, the Markdown parser, the diff viewer, and the syntax
|
|
72
|
+
scanners. ``__all__`` is still the public surface.
|
|
73
|
+
"""
|
|
74
|
+
try:
|
|
75
|
+
module = _EXPORTS[name]
|
|
76
|
+
except KeyError:
|
|
77
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}") from None
|
|
78
|
+
return getattr(importlib.import_module(module, __name__), name)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def __dir__() -> list[str]:
|
|
82
|
+
"""Return the names this package publishes."""
|
|
83
|
+
return sorted(__all__)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
__all__ = [
|
|
87
|
+
"Collapsible",
|
|
88
|
+
"Column",
|
|
89
|
+
"Completer",
|
|
90
|
+
"CompletionItem",
|
|
91
|
+
"CompletionPopup",
|
|
92
|
+
"ConsolePanel",
|
|
93
|
+
"Dialog",
|
|
94
|
+
"DialogAction",
|
|
95
|
+
"DiffLine",
|
|
96
|
+
"DiffSegment",
|
|
97
|
+
"DiffView",
|
|
98
|
+
"ListItem",
|
|
99
|
+
"ListView",
|
|
100
|
+
"ListPage",
|
|
101
|
+
"Markdown",
|
|
102
|
+
"MarkdownSource",
|
|
103
|
+
"MarkdownView",
|
|
104
|
+
"ProgressBar",
|
|
105
|
+
"RichText",
|
|
106
|
+
"Rule",
|
|
107
|
+
"Spinner",
|
|
108
|
+
"StatusBar",
|
|
109
|
+
"Table",
|
|
110
|
+
"TaskPanel",
|
|
111
|
+
"Text",
|
|
112
|
+
"TextArea",
|
|
113
|
+
"Toast",
|
|
114
|
+
"build_unified",
|
|
115
|
+
"layout_input",
|
|
116
|
+
"next_word",
|
|
117
|
+
"parse_unified",
|
|
118
|
+
"previous_word",
|
|
119
|
+
"render_markdown",
|
|
120
|
+
"word_diff",
|
|
121
|
+
]
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""A header that expands to reveal one child."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
|
|
7
|
+
from zettcode.tui import EXPANDED, MARKER
|
|
8
|
+
|
|
9
|
+
from ..core.events import KeyEvent, MouseAction, MouseEvent
|
|
10
|
+
from ..core.geometry import Constraints, Rect, Size
|
|
11
|
+
from ..core.host import Host
|
|
12
|
+
from ..core.widget import Widget
|
|
13
|
+
from ..render import Canvas, Style, truncate
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Collapsible(Widget):
|
|
17
|
+
"""Show a title row and, when expanded, a body beneath it.
|
|
18
|
+
|
|
19
|
+
Shape::
|
|
20
|
+
|
|
21
|
+
> Thinking <- collapsed: U+25B8, one row only
|
|
22
|
+
v Thinking <- expanded: U+25BE
|
|
23
|
+
Checked the renderer. <- body, directly beneath the header
|
|
24
|
+
|
|
25
|
+
Expanding changes the widget's height, so toggling calls
|
|
26
|
+
``host.request_layout()``; the body is only a child while it is expanded,
|
|
27
|
+
which keeps it out of layout and hit testing when collapsed.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
MARKERS = (MARKER, EXPANDED)
|
|
31
|
+
|
|
32
|
+
def __init__(
|
|
33
|
+
self,
|
|
34
|
+
title: str = "",
|
|
35
|
+
body: Widget | None = None,
|
|
36
|
+
*,
|
|
37
|
+
expanded: bool = False,
|
|
38
|
+
on_toggle: Callable[[bool], None] | None = None,
|
|
39
|
+
) -> None:
|
|
40
|
+
"""Configure the title, the body, and the initial disclosure state.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
title: Header text drawn after the disclosure marker.
|
|
44
|
+
body: Child revealed below the header while expanded; ``None`` makes
|
|
45
|
+
the widget a pure header.
|
|
46
|
+
expanded: Whether the body starts visible.
|
|
47
|
+
on_toggle: Called with the new state after every toggle.
|
|
48
|
+
"""
|
|
49
|
+
super().__init__()
|
|
50
|
+
self.title = title
|
|
51
|
+
self.body = body
|
|
52
|
+
self.expanded = expanded
|
|
53
|
+
self.on_toggle = on_toggle
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def focusable(self) -> bool:
|
|
57
|
+
"""Take focus so Enter and Space can toggle the header."""
|
|
58
|
+
return True
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def children(self) -> tuple[Widget, ...]:
|
|
62
|
+
"""Expose the body only while it is expanded."""
|
|
63
|
+
return (self.body,) if self.body is not None and self.expanded else ()
|
|
64
|
+
|
|
65
|
+
def toggle(self) -> None:
|
|
66
|
+
"""Flip the disclosure state and tell the owner about it."""
|
|
67
|
+
self.expanded = not self.expanded
|
|
68
|
+
if self.on_toggle is not None:
|
|
69
|
+
self.on_toggle(self.expanded)
|
|
70
|
+
|
|
71
|
+
def measure(self, constraints: Constraints) -> Size:
|
|
72
|
+
"""Ask for the header row, plus the body while it is expanded."""
|
|
73
|
+
if self.body is None or not self.expanded:
|
|
74
|
+
return constraints.constrain(Size(len(self.title) + 2, 1))
|
|
75
|
+
inner = self.body.measure(constraints)
|
|
76
|
+
return constraints.constrain(Size(max(len(self.title) + 2, inner.width), 1 + inner.height))
|
|
77
|
+
|
|
78
|
+
def layout(self, rect: Rect) -> None:
|
|
79
|
+
"""Reserve the first row for the header and give the rest to the body."""
|
|
80
|
+
super().layout(rect)
|
|
81
|
+
if self.body is not None and self.expanded:
|
|
82
|
+
self.body.layout(Rect(rect.x, rect.y + 1, rect.width, max(0, rect.height - 1)))
|
|
83
|
+
|
|
84
|
+
def render(self, canvas: Canvas) -> None:
|
|
85
|
+
"""Paint the header marker and title, then the body when expanded."""
|
|
86
|
+
if self.rect.empty:
|
|
87
|
+
return
|
|
88
|
+
theme = self.theme
|
|
89
|
+
marker = self.MARKERS[1] if self.expanded else self.MARKERS[0]
|
|
90
|
+
canvas.draw_text(
|
|
91
|
+
self.rect.x,
|
|
92
|
+
self.rect.y,
|
|
93
|
+
truncate(f"{marker} {self.title}", self.rect.width),
|
|
94
|
+
Style(foreground=theme.accent, bold=True),
|
|
95
|
+
max_width=self.rect.width,
|
|
96
|
+
)
|
|
97
|
+
if self.body is not None and self.expanded:
|
|
98
|
+
self.body.render(canvas)
|
|
99
|
+
|
|
100
|
+
def handle(self, event, host: Host) -> bool:
|
|
101
|
+
"""Toggle on a click on the header row, or on Enter and Space."""
|
|
102
|
+
if isinstance(event, MouseEvent):
|
|
103
|
+
if self.rect.contains(event.x, event.y) and event.y == self.rect.y:
|
|
104
|
+
if event.action is MouseAction.DOWN:
|
|
105
|
+
host.focus(self)
|
|
106
|
+
self.toggle()
|
|
107
|
+
host.request_layout()
|
|
108
|
+
return True
|
|
109
|
+
return False
|
|
110
|
+
if not isinstance(event, KeyEvent) or host.focused_widget() is not self:
|
|
111
|
+
return False
|
|
112
|
+
if event.key in ("enter", "space"):
|
|
113
|
+
self.toggle()
|
|
114
|
+
host.request_layout()
|
|
115
|
+
return True
|
|
116
|
+
return False
|