termux-colors 0.1.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 (39) hide show
  1. termux_colors-0.1.0/PKG-INFO +92 -0
  2. termux_colors-0.1.0/README.md +79 -0
  3. termux_colors-0.1.0/pyproject.toml +31 -0
  4. termux_colors-0.1.0/setup.cfg +4 -0
  5. termux_colors-0.1.0/src/termux_colors/__init__.py +3 -0
  6. termux_colors-0.1.0/src/termux_colors/__main__.py +4 -0
  7. termux_colors-0.1.0/src/termux_colors/app.py +244 -0
  8. termux_colors-0.1.0/src/termux_colors/library.py +114 -0
  9. termux_colors-0.1.0/src/termux_colors/models/__init__.py +0 -0
  10. termux_colors-0.1.0/src/termux_colors/models/app_theme.py +137 -0
  11. termux_colors-0.1.0/src/termux_colors/models/colors.py +121 -0
  12. termux_colors-0.1.0/src/termux_colors/paths.py +38 -0
  13. termux_colors-0.1.0/src/termux_colors/persistence/__init__.py +0 -0
  14. termux_colors-0.1.0/src/termux_colors/persistence/backup.py +40 -0
  15. termux_colors-0.1.0/src/termux_colors/persistence/colors_io.py +74 -0
  16. termux_colors-0.1.0/src/termux_colors/persistence/properties.py +75 -0
  17. termux_colors-0.1.0/src/termux_colors/persistence/theme_io.py +44 -0
  18. termux_colors-0.1.0/src/termux_colors/presets/__init__.py +0 -0
  19. termux_colors-0.1.0/src/termux_colors/presets/built_in.py +95 -0
  20. termux_colors-0.1.0/src/termux_colors/screens/__init__.py +0 -0
  21. termux_colors-0.1.0/src/termux_colors/screens/app_theme_editor.py +150 -0
  22. termux_colors-0.1.0/src/termux_colors/screens/dialogs.py +127 -0
  23. termux_colors-0.1.0/src/termux_colors/screens/main_menu.py +79 -0
  24. termux_colors-0.1.0/src/termux_colors/screens/preset_picker.py +234 -0
  25. termux_colors-0.1.0/src/termux_colors/screens/terminal_editor.py +229 -0
  26. termux_colors-0.1.0/src/termux_colors/theme.py +135 -0
  27. termux_colors-0.1.0/src/termux_colors/widgets.py +179 -0
  28. termux_colors-0.1.0/src/termux_colors.egg-info/PKG-INFO +92 -0
  29. termux_colors-0.1.0/src/termux_colors.egg-info/SOURCES.txt +37 -0
  30. termux_colors-0.1.0/src/termux_colors.egg-info/dependency_links.txt +1 -0
  31. termux_colors-0.1.0/src/termux_colors.egg-info/entry_points.txt +2 -0
  32. termux_colors-0.1.0/src/termux_colors.egg-info/requires.txt +4 -0
  33. termux_colors-0.1.0/src/termux_colors.egg-info/top_level.txt +1 -0
  34. termux_colors-0.1.0/tests/test_app_smoke.py +93 -0
  35. termux_colors-0.1.0/tests/test_backup.py +50 -0
  36. termux_colors-0.1.0/tests/test_colors_io.py +80 -0
  37. termux_colors-0.1.0/tests/test_library.py +105 -0
  38. termux_colors-0.1.0/tests/test_theme.py +93 -0
  39. termux_colors-0.1.0/tests/test_theme_io.py +99 -0
@@ -0,0 +1,92 @@
1
+ Metadata-Version: 2.4
2
+ Name: termux-colors
3
+ Version: 0.1.0
4
+ Summary: A Textual TUI to change Termux terminal colors and app theme
5
+ Author-email: Ron Sloan <xkiller1177@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/GeneralKaos666/color-changer-termux
8
+ Requires-Python: >=3.10
9
+ Description-Content-Type: text/markdown
10
+ Requires-Dist: textual>=0.60
11
+ Provides-Extra: dev
12
+ Requires-Dist: pytest>=7; extra == "dev"
13
+
14
+ # termux-colors
15
+
16
+ A keyboard-driven TUI for [Termux](https://termux.dev) that changes your
17
+ terminal colour scheme and your Termux app theme.
18
+
19
+ ```
20
+ ┌─────────────────────────────┐
21
+ │ Termux Color Changer │
22
+ │ [ Preset Themes ] │
23
+ │ [ Terminal Colors ] │
24
+ │ [ App Theme ] │
25
+ │ [ Quit ] │
26
+ └─────────────────────────────┘
27
+ ```
28
+
29
+ ## Features
30
+
31
+ - **Themes itself from your palette** — the TUI re-colours itself to match the
32
+ active terminal theme, and updates live as you highlight presets.
33
+ - **Preset Themes** — browses the themes in `~/.termux/colors/` (the same
34
+ library used by `colors.sh`), with a filter box, a mock shell session
35
+ rendered in each theme, a swatch grid, and a colour strip. Activating a theme
36
+ points `~/.termux/colors.properties` at it and runs `termux-reload-settings`.
37
+ - **Terminal Colors** — edits background, foreground, cursor and the 16 ANSI
38
+ colours with inline swatches, live validation and a live preview. Save in
39
+ place or `Save As...` a new theme.
40
+ - **App Theme** — edits a safe subset of `~/.termux/termux.properties`
41
+ (`use-black-ui`, cursor style, margins, transcript rows, etc.) in sectioned
42
+ groups, without touching anything it does not manage.
43
+ - **Safe by default** — before overwriting an existing config it asks whether
44
+ to write a timestamped backup to `~/.termux/backups/`.
45
+
46
+ ## Install
47
+
48
+ Requires Python 3.10+ (Termux: `pkg install python`).
49
+
50
+ ```sh
51
+ pip install .
52
+ ```
53
+
54
+ This installs a `termux-colors` command on your `PATH`.
55
+
56
+ ## Usage
57
+
58
+ ```sh
59
+ termux-colors
60
+ ```
61
+
62
+ Navigation: `Tab`/`Shift+Tab` moves focus, `Enter` activates a button,
63
+ `Esc` goes back, `q` quits from the main menu. In Preset Themes, focus the
64
+ filter box and type to search the library.
65
+
66
+ ## Relationship to `colors.sh`
67
+
68
+ `termux-colors` reads the same library as `~/.termux/colors.sh`
69
+ (`~/.termux/colors/<group>/*.properties`) and activates themes the same way:
70
+ by symlinking `~/.termux/colors.properties` and calling
71
+ `termux-reload-settings`. The two can be used interchangeably. If the library
72
+ is empty, a small set of built-in themes is written to
73
+ `~/.termux/colors/builtin/`.
74
+
75
+ `termux.properties` is edited line-by-line, so complex settings such as
76
+ `extra-keys`, background images and comments are left untouched.
77
+
78
+ ## Development
79
+
80
+ ```sh
81
+ python -m venv .venv
82
+ .venv/bin/pip install -e '.[dev]'
83
+ .venv/bin/python -m pytest
84
+ ```
85
+
86
+ Tests point the app at a temporary directory through the `TERMUX_HOME`
87
+ environment variable, so they never touch your real config.
88
+
89
+ ## Backups
90
+
91
+ Backups are plain copies named `colors-YYYY-MM-DD-HHMMSS.properties` or
92
+ `termux-YYYY-MM-DD-HHMMSS.properties` under `~/.termux/backups/`.
@@ -0,0 +1,79 @@
1
+ # termux-colors
2
+
3
+ A keyboard-driven TUI for [Termux](https://termux.dev) that changes your
4
+ terminal colour scheme and your Termux app theme.
5
+
6
+ ```
7
+ ┌─────────────────────────────┐
8
+ │ Termux Color Changer │
9
+ │ [ Preset Themes ] │
10
+ │ [ Terminal Colors ] │
11
+ │ [ App Theme ] │
12
+ │ [ Quit ] │
13
+ └─────────────────────────────┘
14
+ ```
15
+
16
+ ## Features
17
+
18
+ - **Themes itself from your palette** — the TUI re-colours itself to match the
19
+ active terminal theme, and updates live as you highlight presets.
20
+ - **Preset Themes** — browses the themes in `~/.termux/colors/` (the same
21
+ library used by `colors.sh`), with a filter box, a mock shell session
22
+ rendered in each theme, a swatch grid, and a colour strip. Activating a theme
23
+ points `~/.termux/colors.properties` at it and runs `termux-reload-settings`.
24
+ - **Terminal Colors** — edits background, foreground, cursor and the 16 ANSI
25
+ colours with inline swatches, live validation and a live preview. Save in
26
+ place or `Save As...` a new theme.
27
+ - **App Theme** — edits a safe subset of `~/.termux/termux.properties`
28
+ (`use-black-ui`, cursor style, margins, transcript rows, etc.) in sectioned
29
+ groups, without touching anything it does not manage.
30
+ - **Safe by default** — before overwriting an existing config it asks whether
31
+ to write a timestamped backup to `~/.termux/backups/`.
32
+
33
+ ## Install
34
+
35
+ Requires Python 3.10+ (Termux: `pkg install python`).
36
+
37
+ ```sh
38
+ pip install .
39
+ ```
40
+
41
+ This installs a `termux-colors` command on your `PATH`.
42
+
43
+ ## Usage
44
+
45
+ ```sh
46
+ termux-colors
47
+ ```
48
+
49
+ Navigation: `Tab`/`Shift+Tab` moves focus, `Enter` activates a button,
50
+ `Esc` goes back, `q` quits from the main menu. In Preset Themes, focus the
51
+ filter box and type to search the library.
52
+
53
+ ## Relationship to `colors.sh`
54
+
55
+ `termux-colors` reads the same library as `~/.termux/colors.sh`
56
+ (`~/.termux/colors/<group>/*.properties`) and activates themes the same way:
57
+ by symlinking `~/.termux/colors.properties` and calling
58
+ `termux-reload-settings`. The two can be used interchangeably. If the library
59
+ is empty, a small set of built-in themes is written to
60
+ `~/.termux/colors/builtin/`.
61
+
62
+ `termux.properties` is edited line-by-line, so complex settings such as
63
+ `extra-keys`, background images and comments are left untouched.
64
+
65
+ ## Development
66
+
67
+ ```sh
68
+ python -m venv .venv
69
+ .venv/bin/pip install -e '.[dev]'
70
+ .venv/bin/python -m pytest
71
+ ```
72
+
73
+ Tests point the app at a temporary directory through the `TERMUX_HOME`
74
+ environment variable, so they never touch your real config.
75
+
76
+ ## Backups
77
+
78
+ Backups are plain copies named `colors-YYYY-MM-DD-HHMMSS.properties` or
79
+ `termux-YYYY-MM-DD-HHMMSS.properties` under `~/.termux/backups/`.
@@ -0,0 +1,31 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "termux-colors"
7
+ version = "0.1.0"
8
+ description = "A Textual TUI to change Termux terminal colors and app theme"
9
+ readme = "README.md"
10
+ authors = [
11
+ { name="Ron Sloan", email="xkiller1177@gmail.com" },
12
+ ]
13
+ requires-python = ">=3.10"
14
+ license = { text = "MIT" }
15
+ dependencies = ["textual>=0.60"]
16
+
17
+ [project.urls]
18
+ "Homepage" = "https://github.com/GeneralKaos666/color-changer-termux"
19
+
20
+ [project.optional-dependencies]
21
+ dev = ["pytest>=7"]
22
+
23
+ [project.scripts]
24
+ termux-colors = "termux_colors.app:main"
25
+
26
+ [tool.setuptools.packages.find]
27
+ where = ["src"]
28
+
29
+ [tool.pytest.ini_options]
30
+ testpaths = ["tests"]
31
+ pythonpath = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ """Termux color changer TUI."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,4 @@
1
+ from .app import main
2
+
3
+ if __name__ == "__main__":
4
+ main()
@@ -0,0 +1,244 @@
1
+ """Textual application entry point."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from textual.app import App
6
+
7
+ from .library import active_colors
8
+ from .screens.app_theme_editor import AppThemeEditorScreen
9
+ from .screens.main_menu import MainMenuScreen
10
+ from .screens.preset_picker import PresetPickerScreen
11
+ from .screens.terminal_editor import TerminalEditorScreen
12
+ from .theme import apply_app_theme
13
+
14
+ CSS = """
15
+ Screen {
16
+ background: $background;
17
+ color: $foreground;
18
+ }
19
+
20
+ Header {
21
+ background: $panel;
22
+ color: $foreground;
23
+ }
24
+
25
+ Footer {
26
+ background: $panel;
27
+ }
28
+
29
+ #menu-container {
30
+ align: center top;
31
+ padding: 1 2;
32
+ }
33
+
34
+ #banner {
35
+ text-align: center;
36
+ width: 100%;
37
+ }
38
+
39
+ #tagline {
40
+ text-align: center;
41
+ color: $text-muted;
42
+ width: 100%;
43
+ padding-bottom: 1;
44
+ }
45
+
46
+ #strip {
47
+ text-align: center;
48
+ width: 100%;
49
+ }
50
+
51
+ #status {
52
+ text-align: center;
53
+ color: $text-muted;
54
+ width: 100%;
55
+ padding: 1 0;
56
+ }
57
+
58
+ #menu {
59
+ width: 62;
60
+ max-width: 100%;
61
+ height: auto;
62
+ border: round $primary;
63
+ border-title-color: $primary;
64
+ border-title-align: center;
65
+ padding: 1 2;
66
+ background: $surface;
67
+ }
68
+
69
+ #menu Button {
70
+ width: 100%;
71
+ margin-bottom: 1;
72
+ text-style: bold;
73
+ }
74
+
75
+ .panel-title {
76
+ text-style: bold;
77
+ background: $boost;
78
+ color: $foreground;
79
+ width: 100%;
80
+ padding: 0 1;
81
+ }
82
+
83
+ .hint {
84
+ color: $text-muted;
85
+ }
86
+
87
+ #picker {
88
+ height: 1fr;
89
+ }
90
+
91
+ #picker-list {
92
+ width: 42%;
93
+ min-width: 26;
94
+ border: round $primary;
95
+ border-title-color: $primary;
96
+ padding: 0 1;
97
+ background: $surface;
98
+ }
99
+
100
+ #theme-filter {
101
+ margin: 1 0;
102
+ }
103
+
104
+ #picker-preview {
105
+ width: 58%;
106
+ border: round $secondary;
107
+ border-title-color: $secondary;
108
+ padding: 1 2;
109
+ background: $surface;
110
+ }
111
+
112
+ #details {
113
+ color: $text-muted;
114
+ padding-top: 1;
115
+ }
116
+
117
+ #editor {
118
+ height: 1fr;
119
+ }
120
+
121
+ #editor-form {
122
+ width: 52%;
123
+ min-width: 32;
124
+ border: round $primary;
125
+ border-title-color: $primary;
126
+ padding: 0 1 1 1;
127
+ background: $surface;
128
+ }
129
+
130
+ #editor-preview {
131
+ width: 48%;
132
+ border: round $secondary;
133
+ border-title-color: $secondary;
134
+ padding: 1 2;
135
+ background: $surface;
136
+ }
137
+
138
+ .field-label {
139
+ color: $text-muted;
140
+ text-style: bold;
141
+ padding-top: 1;
142
+ }
143
+
144
+ .field-row {
145
+ height: auto;
146
+ }
147
+
148
+ .field-row Input {
149
+ width: 1fr;
150
+ }
151
+
152
+ .field-swatch {
153
+ width: 4;
154
+ height: 1;
155
+ margin: 0 1 0 0;
156
+ }
157
+
158
+ .input-error {
159
+ border: tall $error;
160
+ }
161
+
162
+ .section-rule {
163
+ color: $accent;
164
+ padding-top: 1;
165
+ }
166
+
167
+ #actions {
168
+ height: auto;
169
+ padding: 1 0;
170
+ align-horizontal: center;
171
+ }
172
+
173
+ #actions Button {
174
+ margin: 0 1;
175
+ min-width: 12;
176
+ }
177
+
178
+ ConfirmScreen, MessageScreen, PromptScreen {
179
+ align: center middle;
180
+ background: $background 70%;
181
+ }
182
+
183
+ #dialog {
184
+ width: 64;
185
+ max-width: 90%;
186
+ height: auto;
187
+ padding: 1 2;
188
+ background: $panel;
189
+ border: round $primary;
190
+ border-title-color: $primary;
191
+ }
192
+
193
+ #dialog-title {
194
+ text-style: bold;
195
+ color: $primary;
196
+ padding-bottom: 1;
197
+ }
198
+
199
+ #dialog-message {
200
+ padding-bottom: 1;
201
+ }
202
+
203
+ #dialog-buttons {
204
+ height: auto;
205
+ align-horizontal: right;
206
+ padding-top: 1;
207
+ }
208
+
209
+ #dialog-buttons Button {
210
+ margin-left: 1;
211
+ }
212
+
213
+ Toast {
214
+ background: $panel;
215
+ border: round $primary;
216
+ color: $foreground;
217
+ }
218
+ """
219
+
220
+
221
+ class TermuxColorsApp(App):
222
+ """Manage Termux terminal colours and app theme."""
223
+
224
+ CSS = CSS
225
+ TITLE = "Termux Colors"
226
+ SCREENS = {
227
+ "main": MainMenuScreen,
228
+ "presets": PresetPickerScreen,
229
+ "terminal": TerminalEditorScreen,
230
+ "app-theme": AppThemeEditorScreen,
231
+ }
232
+
233
+ def on_mount(self) -> None:
234
+ apply_app_theme(self, active_colors())
235
+ self.push_screen("main")
236
+
237
+
238
+ def main() -> None:
239
+ """Console-script entry point."""
240
+ TermuxColorsApp().run()
241
+
242
+
243
+ if __name__ == "__main__":
244
+ main()
@@ -0,0 +1,114 @@
1
+ """Scan the user's theme library and activate themes by symlink."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import shutil
7
+ import subprocess
8
+ from dataclasses import dataclass
9
+ from pathlib import Path
10
+
11
+
12
+ @dataclass(frozen=True)
13
+ class Theme:
14
+ """One theme file discovered in the colour library."""
15
+
16
+ name: str
17
+ path: Path
18
+ group: str = "root"
19
+
20
+ @property
21
+ def label(self) -> str:
22
+ if self.group and self.group != "root":
23
+ return f"{self.group}/{self.name}"
24
+ return self.name
25
+
26
+
27
+ def _is_backup(path: Path) -> bool:
28
+ name = path.name.lower()
29
+ return "backup" in name or name.endswith(".bak") or ".bak-" in name
30
+
31
+
32
+ def scan_themes(root: Path) -> list[Theme]:
33
+ """Find every usable ``*.properties`` theme under *root*.
34
+
35
+ The active ``colors.properties`` file, backups, and dotfiles are skipped.
36
+ Results are ordered by group then name.
37
+ """
38
+ if not root.is_dir():
39
+ return []
40
+ themes: list[Theme] = []
41
+ for path in root.rglob("*.properties"):
42
+ if not path.is_file() and not path.is_symlink():
43
+ continue
44
+ if path.name == "colors.properties" or _is_backup(path):
45
+ continue
46
+ relative = path.relative_to(root)
47
+ group = relative.parts[0] if len(relative.parts) > 1 else "root"
48
+ if group.startswith("."):
49
+ continue
50
+ themes.append(Theme(name=path.stem, path=path, group=group))
51
+ themes.sort(key=lambda theme: ((theme.group == "root"), theme.group.lower(), theme.name.lower()))
52
+ return themes
53
+
54
+
55
+ def group_themes(themes: list[Theme]) -> dict[str, list[Theme]]:
56
+ """Group themes by their library sub-directory."""
57
+ grouped: dict[str, list[Theme]] = {}
58
+ for theme in themes:
59
+ grouped.setdefault(theme.group, []).append(theme)
60
+ return grouped
61
+
62
+
63
+ def active_theme_path(colors_properties: Path) -> Path | None:
64
+ """Return the real theme file the active ``colors.properties`` resolves to."""
65
+ if not colors_properties.exists() and not colors_properties.is_symlink():
66
+ return None
67
+ return Path(os.path.realpath(colors_properties))
68
+
69
+
70
+ def active_colors():
71
+ """Return the colours currently configured, or the defaults when unset."""
72
+ from .models.colors import TerminalColors
73
+ from .paths import colors_properties_path
74
+ from .persistence.colors_io import read_colors
75
+
76
+ path = colors_properties_path()
77
+ if path.exists() or path.is_symlink():
78
+ try:
79
+ return read_colors(path)
80
+ except OSError:
81
+ pass
82
+ return TerminalColors()
83
+
84
+
85
+ def apply_theme(theme_path: Path, colors_properties: Path) -> None:
86
+ """Point ``colors.properties`` at *theme_path* and reload Termux.
87
+
88
+ An existing file or symlink is replaced. ``colors.properties`` becomes a
89
+ symlink into the library, matching the layout used by ``colors.sh``. If
90
+ the target already is ``colors.properties`` itself (a plain file the user
91
+ edits in place) this is a no-op so no self-referential link is created.
92
+ """
93
+ theme_path = theme_path.resolve()
94
+ if not theme_path.is_file():
95
+ raise FileNotFoundError(f"theme not found: {theme_path}")
96
+ if colors_properties.exists() or colors_properties.is_symlink():
97
+ if colors_properties.resolve() == theme_path:
98
+ return
99
+ colors_properties.parent.mkdir(parents=True, exist_ok=True)
100
+ if colors_properties.is_symlink() or colors_properties.exists():
101
+ colors_properties.unlink()
102
+ os.symlink(theme_path, colors_properties)
103
+
104
+
105
+ def reload_termux() -> bool:
106
+ """Run ``termux-reload-settings`` when available; return success."""
107
+ executable = shutil.which("termux-reload-settings")
108
+ if not executable:
109
+ return False
110
+ try:
111
+ subprocess.run([executable], check=True, timeout=15)
112
+ except (subprocess.SubprocessError, OSError):
113
+ return False
114
+ return True