Skip to content

Commit

Permalink
Switch to Textual 0.86.
Browse files Browse the repository at this point in the history
  • Loading branch information
xavierog committed Dec 17, 2024
1 parent b8693ba commit 03824dd
Show file tree
Hide file tree
Showing 81 changed files with 353 additions and 252 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) for its CLI (Command-Line Interface), i.e. for the `moulti` command.
Although Moulti's Python packages, modules and functions are obviously available, they do not constitute a public API yet.

## Unreleased

## Changed

- Moulti now requires Textual == 0.86.3

## [1.23.2] - 2024-12-17

### Fixed
Expand Down
10 changes: 7 additions & 3 deletions Documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ This document assumes:
Moulti requires:
- Linux, BSD or MacOS
- Python ≥ 3.10
- Textual ≥ 0.83
- Textual 0.86.3

## Installation

Expand Down Expand Up @@ -1087,6 +1087,9 @@ The snippets below demonstrate how to leverage this mechanism to define one's st
```css
/* Define a step class named "customstate" rendered with red text on a yellow background: */
Step.customstate {
& CollapsibleTitle {
color: red;
}
color: red;
background: yellow;
& MoultiLog { scrollbar-corner-color: yellow; }
Expand Down Expand Up @@ -1130,9 +1133,10 @@ Moulti requires Python ≥ 3.10 because:
- Python 3.9 [isn't receiving regular bug fixes anymore](https://www.python.org/downloads/release/python-3918/).
- Moulti type hints use PEP 604 (Allow writing union types as X | Y) syntax, which requires Python ≥ 3.10.

The exact version of Textual required to run Moulti is 0.83.0.
The exact version of Textual required to run Moulti is 0.86.3.
Details:
- version 0.84.0 introduce a rendering issue that affects focus indicators
- version 0.86.0 replaces the dark vs light modes with themes; Moulti went through this breaking change on its way to Textual 1.0.0
- version 0.84.0 introduces a rendering issue that affects focus indicators
- version 0.83.0 removes the need to override `Screen.ALLOW_IN_MAXIMIZED_VIEW`
- version 0.79.0 supports maximizing widgets
- version 0.76.0 supports dynamic keybindings
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ git+https://github.com/Textualize/pytest-textual-snapshot@19b2607c93b898cc67c6f9
pytest-xdist
ruff
textual-dev
textual==0.83.0
textual==0.86.3
twine
unidiff
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
keywords=['cli', 'tui', 'curses', 'terminal', 'multiplex', 'script', 'output', 'steps', 'textual', 'collapsible', 'ansible', 'diff'],
package_dir={'': 'src'},
python_requires='>=3.10',
install_requires=['textual==0.83.0', 'pyperclip', 'argcomplete', 'unidiff'],
install_requires=['textual==0.86.3', 'pyperclip', 'argcomplete', 'unidiff'],
entry_points={
'console_scripts': [
'moulti = moulti.precli:main',
Expand Down
26 changes: 22 additions & 4 deletions src/moulti/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from textual.binding import Binding
from textual.css.query import NoMatches
from textual.dom import BadIdentifier
from textual.reactive import reactive
from textual.widgets import Label, ProgressBar
from textual.worker import get_current_worker, NoActiveWorker
from . import __version__ as MOULTI_VERSION
Expand All @@ -32,6 +33,7 @@
from .protocol import MoultiConnectionClosedException, MoultiProtocolException, Message, FDs
from .protocol import MoultiTLVReader, data_to_message, getraddr
from .search import TextSearch, MatchSpan
from .themes import MOULTI_THEMES
from .widgets import MoultiWidgetException
from .widgets.tui import MoultiWidgets
from .widgets.footer import Footer
Expand Down Expand Up @@ -149,6 +151,9 @@ class Moulti(App):
# Do not minimize on 'escape' because widgets need to restore their max height:
ESCAPE_TO_MINIMIZE = False

dark = reactive(True)
"""True for dark mode, False for light mode."""

def __init__(self, command: list[str]|None = None, instance_name: str|None = None):
self.instance_name = instance_name
self.socket_path = default_moulti_socket_path(instance_name) if instance_name else PRINTABLE_MOULTI_SOCKET
Expand Down Expand Up @@ -274,6 +279,11 @@ def compose(self) -> ComposeResult:
yield self.footer
yield self.end_user_console

def on_mount(self) -> None:
for theme in MOULTI_THEMES.values():
self.register_theme(theme)
self.theme = 'moulti-dark' if self.dark else 'moulti-light'

def on_ready(self) -> None:
self.logconsole(f'Moulti v{MOULTI_VERSION}, Textual v{TEXTUAL_VERSION}, Python v{sys.version}')
self.logconsole(f'instance "{current_instance()}", PID {os.getpid()}')
Expand Down Expand Up @@ -473,10 +483,12 @@ def logconsole(self, line: str) -> None:
self.call_from_thread(self.end_user_console.write, line)

def action_light_mode(self) -> None:
self.theme = 'moulti-light'
self.dark = False
self.refresh_bindings()

def action_dark_mode(self) -> None:
self.theme = 'moulti-dark'
self.dark = True
self.refresh_bindings()

Expand Down Expand Up @@ -849,7 +861,7 @@ def accept(socket: Socket) -> None:
/* Styles inherited by all widgets: */
$scrollbar_background: #b2b2b2;
$scrollbar_inactive_bar: #686868;
$scrollbar_active_bar: $accent-darken-1;
$scrollbar_active_bar: $primary-darken-1;
Widget {
/* By default, in dark mode, scrollbars are not rendered clearly; enforce their colours: */
scrollbar-background: $scrollbar_background;
Expand All @@ -859,26 +871,32 @@ def accept(socket: Socket) -> None:
scrollbar-color-active: $scrollbar_active_bar;
scrollbar-color-hover: $scrollbar_active_bar;
}
#textual-tooltip { /* overrides "Tooltip" since it is more specific */
background: $surface;
}
/* One-line title at the top of the screen: */
#header {
text-align: center;
/* For the title to appear centered, the widget must occupy all available width: */
width: 100%;
background: $accent;
color: auto;
background: $primary;
color: $text;
dock: top; /* relevant in maximized mode */
}
/* Show the progress bar as a full-width extension of the footer: */
#progress_bar {
display: none; /* Do not display by default */
background: $accent;
background: $primary;
padding-left: 1;
padding-right: 1;
width: 100%;
&> Bar {
width: 1fr;
&> .bar--bar {
color: $warning;
}
}
}
"""
Expand Down
53 changes: 53 additions & 0 deletions src/moulti/themes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
"""
Moulti themes.
Textual < 0.86.0 used to offer two themes: textual-dark and textual-light,
along with an App.dark reactive attribute to switch between them.
Textual 0.86.0 removed App.dark, introduced more themes, and revamped
semantics, styles and colors.
That breaking change severely affected Moulti. The themes below aim to restore
and preserve Moulti's look'n feel.
"""
from textual.theme import Theme

MOULTI_THEMES = {
'dark': Theme(
name="moulti-dark",
primary="#0178D4",
secondary="#004578",
warning="#ffa62b",
error="#ba3c5b",
success="#4ebf71",
accent="#ffa62b",
foreground="#e0e0e0", #
background="#1e1e1e", #
surface="#121212", #
panel="#24292f", #
boost=None,
dark=True,
luminosity_spread = 0.15,
text_alpha = 0.95,
variables={
"footer-key-foreground": "#ffffff",
},
),
'light': Theme(
name="moulti-light",
primary="#0178D4",
secondary="#004578",
warning="#ffa62b",
error="#ba3c5b",
success="#4ebf71",
accent="#ffa62b",
foreground="#0a0a0a", #
background="#f5f5f5", #
surface="#efefef", #
panel="#dce3e8", #
boost=None,
dark=False,
luminosity_spread = 0.15,
text_alpha = 0.95,
variables={
"footer-key-foreground": "#ffffff",
},
),
}
6 changes: 3 additions & 3 deletions src/moulti/widgets/abstractstep/tui.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ def search_label(self, label: str, search: TextSearch) -> tuple[bool, Text]:
return True, search.highlight(text, *self.label_search_cursor)

DEFAULT_COLORS = """
$step_default: $primary;
$step_default: $secondary;
$step_success: #00ff00; /* bright green */
$step_warning: #ffa500; /* orange */
$step_error: #ff6347; /* tomato */
Expand All @@ -145,13 +145,13 @@ def search_label(self, label: str, search: TextSearch) -> tuple[bool, Text]:
height: auto;
background: $step_default;
border-left: blank;
color: auto;
color: $text;
&.success { background: $step_success; }
&.warning { background: $step_warning; }
&.error { background: $step_error; }
&.inactive { background: $step_inactive; }
}
AbstractStep:focus, AbstractStep:focus-within {
border-left: thick $accent-lighten-3;
border-left: thick $primary-lighten-3;
}
"""
5 changes: 5 additions & 0 deletions src/moulti/widgets/collapsiblestep/tui.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,13 +154,18 @@ def export_properties(self) -> dict[str, Any]:
}
& CollapsibleTitle {
padding: 0;
color: $text;
width: 100%;
}
& CollapsibleTitle:focus {
background: initial;
color: $text;
text-style: none;
}
& CollapsibleTitle:hover {
background: $foreground 80%;
color: $text;
text-style: none;
}
/* Collapsible contents: */
& Contents {
Expand Down
24 changes: 14 additions & 10 deletions src/moulti/widgets/footer.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,32 +18,36 @@ def compose(self) -> ComposeResult:
yield footer_key

FOOTER_KEY_CSS = """
background: $accent;
color: white;
background: $primary;
color: $footer-key-foreground;
.footer-key--description {
color: $footer-key-foreground;
}
.footer-key--key {
background: $accent-darken-2;
color: white;
background: $primary-darken-2;
color: $footer-key-foreground;
}
&:hover {
background: $accent-darken-1;
background: $primary-darken-1;
.footer-key--key {
background: $secondary;
color: white;
background: $accent;
color: $footer-key-foreground;
}
}
&.-disabled {
text-style: dim;
background: $accent;
background: $primary;
&:hover {
.footer-key--key {
background: $accent-darken-2;
background: $primary-darken-2;
}
}
}"""

DEFAULT_CSS = """
Footer {
background: $accent;
color: $footer-key-foreground;
background: $primary;
/* FooterKey is specific to the new Footer and thus does not affect the previous one. */
FooterKey {%s
}
Expand Down
4 changes: 3 additions & 1 deletion src/moulti/widgets/helpscreen.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,12 @@ def dismiss_if_active(self) -> None:
DEFAULT_CSS = """
HelpScreen {
align: center middle;
background: $surface 60%;
}
#main_text {
padding: 1;
border: panel $accent;
background: $surface;
border: panel $primary;
border-title-color: #ffffff;
border-title-align: center;
border-subtitle-color: $text-muted;
Expand Down
6 changes: 4 additions & 2 deletions src/moulti/widgets/moulticonsole.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ async def action_to_clipboard(self, keep_styles: bool = False) -> None:
self.app.notify(message, title=title, severity='information' if res else 'error')

DEFAULT_CSS = """
$moulti_console_color: $accent;
$moulti_console_color: $primary;
MoultiConsole {
Label {
width: 100%;
Expand Down Expand Up @@ -128,9 +128,11 @@ def action_scroll_end(self, *args: Any, **kwargs: Any) -> None:
MoultiConsoleLog {
height: auto;
border-left: blank;
background: $background;
}
MoultiConsoleLog:focus {
border-left: thick $accent-lighten-3;
border-left: thick $primary-lighten-3;
background-tint: white 0%;
}
"""

Expand Down
2 changes: 1 addition & 1 deletion src/moulti/widgets/moultilog.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,6 @@ def scroll_to_search_highlight(self) -> tuple[Region, Spacing]|None:
border-left: blank;
}
MoultiLog:focus {
border-left: thick $accent-lighten-3;
border-left: thick $primary-lighten-3;
}
"""
8 changes: 6 additions & 2 deletions src/moulti/widgets/quitdialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,14 @@ def new_quit_request(self) -> None:
DEFAULT_CSS = """
QuitDialog {
align: center middle;
background: $surface 60%;
& Button {
width: 100%;
}
}
#do_not_quit {
background: $secondary;
}
#quit_dialog_grid {
grid-size: 1 4;
Expand All @@ -90,8 +94,8 @@ def new_quit_request(self) -> None:
padding: 0 1;
width: 65;
height: 15;
border: thick $background 80%;
background: $surface;
border: thick $surface;
background: $background;
&.terminate { /* 1 extra button */
grid-size: 1 5;
grid-rows: 1fr 1fr 1fr 1fr 1;
Expand Down
4 changes: 2 additions & 2 deletions src/moulti/widgets/searchinput.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,11 +218,11 @@ async def on_input_submitted(self, event: Input.Submitted) -> None:

DEFAULT_CSS = """
#search_regex_label {
background: $primary-darken-2;
background: $secondary-darken-2;
color: $text-muted;
}
#search_regex_label.regex {
color: $secondary;
color: $accent;
}
SearchInputWidget {
display: none;
Expand Down
Loading

0 comments on commit 03824dd

Please sign in to comment.