tuitorial.app module#

Top-level package for tuitorial.

class tuitorial.Chapter(title, code, steps)[source]#

Bases: Container

A chapter of a tutorial, containing multiple steps.

property current_step: Step | ImageStep#

Get the current step.

async on_mount()[source]#

Mount the chapter.

Return type:

None

async on_resize()[source]#

Called when the app is resized.

Return type:

None

async update_display()[source]#

Update the display with current focus or image.

Return type:

None

async next_step()[source]#

Handle next focus action.

Return type:

None

async previous_step()[source]#

Handle previous focus action.

Return type:

None

async reset_step()[source]#

Reset to first focus pattern.

Return type:

None

async toggle_dim()[source]#

Toggle dim background.

Return type:

None

compose()[source]#

Compose the chapter display.

Return type:

Iterable[Widget]

can_focus: bool = False#

Widget may receive focus.

can_focus_children: bool = True#

Widget’s children may receive focus.

class tuitorial.Focus(pattern, style=Style(color=Color('yellow', ColorType.STANDARD, number=3), bold=True), type=FocusType.LITERAL, extra=None)[source]#

Bases: object

A pattern to focus on with its style.

pattern: str | Pattern | _RangeTuple | int | _StartsWithTuple | _BetweenTuple#
style: Style = Style(color=Color('yellow', ColorType.STANDARD, number=3), bold=True)#
type: FocusType = 1#
extra: dict | None = None#
classmethod literal(text, style=Style(color=Color('yellow', ColorType.STANDARD, number=3), bold=True), *, word_boundary=False, match_index=None)[source]#

Create a focus for a literal string.

Parameters:
  • text (str) – The text to match

  • style (Style) – The style to apply to the matched text

  • word_boundary (bool) – If True, only match the text when it appears as a word

  • match_index (Union[int, list[int], None]) – If provided, only highlight the nth match (0-based) or matches specified by the list. If None, highlight all matches.

Return type:

Focus

classmethod regex(pattern, style=Style(color=Color('green', ColorType.STANDARD, number=2), bold=True), flags=re.MULTILINE)[source]#

Create a focus for a regular expression.

Return type:

Focus

classmethod line(line_number, style=Style(color=Color('cyan', ColorType.STANDARD, number=6), bold=True))[source]#

Create a focus for a line number.

Return type:

Focus

classmethod range(start, end, style=Style(color=Color('magenta', ColorType.STANDARD, number=5), bold=True))[source]#

Create a focus for a range of characters.

Return type:

Focus

classmethod startswith(text, style=Style(color=Color('blue', ColorType.STANDARD, number=4), bold=True), *, from_start_of_line=False)[source]#

Create a focus for text that starts with the given pattern.

Parameters:
  • text (str) – The text to match at the start

  • style (Style) – The style to apply to the matched text

  • from_start_of_line (bool) – If True, only match at the start of lines, if False match anywhere

Return type:

Focus

classmethod between(start_pattern, end_pattern, style=Style(color=Color('blue', ColorType.STANDARD, number=4), bold=True), *, inclusive=True, multiline=True, match_index=None, greedy=False)[source]#

Create a focus for text between two patterns.

Parameters:
  • start_pattern (str) – The pattern marking the start of the region

  • end_pattern (str) – The pattern marking the end of the region

  • style (Style) – The style to apply to the matched text

  • inclusive (bool) – If True, include the start and end patterns in the highlighting

  • multiline (bool) – If True, match across multiple lines

  • match_index (Optional[int]) – If provided, only highlight the nth match (0-based). If None, highlight all matches.

  • greedy (bool) – If True, use greedy matching (matches longest possible string). If False, use non-greedy matching (matches shortest possible string).

Return type:

Focus

classmethod line_containing(pattern, style=Style(color=Color('yellow', ColorType.STANDARD, number=3), bold=True), *, lines_before=0, lines_after=0, regex=False, match_index=None)[source]#

Select the entire line containing a pattern and optionally surrounding lines.

Parameters:
  • pattern (str) – The text pattern to search for.

  • style (Style | str) – The style to apply to the matched lines.

  • lines_before (int) – Number of lines to include before the matched line.

  • lines_after (int) – Number of lines to include after the matched line.

  • regex (bool) – If True, treat pattern as a regular expression.

  • match_index (Optional[int]) – If provided, only highlight the nth match (0-based). If None, highlight all matches.

Return type:

Focus

classmethod syntax(lexer='python', *, theme=None, line_numbers=False, start_line=None, end_line=None)[source]#

Use Rich’s syntax highlighting.

Parameters:
  • lexer (str) – The language to use for syntax highlighting (default: β€œpython”)

  • theme (Optional[str]) – The color theme to use (default: None, uses terminal colors)

  • line_numbers (bool) – Whether to show line numbers

  • start_line (Optional[int]) – First line to highlight (0-based), if None highlight from start

  • end_line (Optional[int]) – Last line to highlight (0-based), if None highlight until end

Return type:

Focus

classmethod markdown()[source]#

Create a focus for a Markdown block.

Return type:

Focus

validate(focuses)[source]#

Validate that there’s at most one markdown or syntax focus.

Return type:

None

class tuitorial.ImageStep(description, image, width=None, height=None, halign=None)[source]#

Bases: object

A step that displays an image.

description: str#
image: str | Path | Image#
width: int | str | None = None#
height: int | str | None = None#
halign: Optional[Literal['left', 'center', 'right']] = None#
class tuitorial.Step(description: str, focuses: list[Focus])[source]#

Bases: NamedTuple

A single step in a tutorial, containing a description and focus patterns.

description: str#

Alias for field number 0

focuses: list[Focus]#

Alias for field number 1

class tuitorial.TitleSlide(title, subtitle=None, font='ansi_shadow', gradient='lava')[source]#

Bases: Container

A title slide with ASCII art and centered text.

compose()[source]#

Compose the title slide.

Return type:

Iterable[Widget]

on_mount()[source]#

Create and display the ASCII art.

Return type:

None

can_focus: bool = False#

Widget may receive focus.

can_focus_children: bool = True#

Widget’s children may receive focus.

class tuitorial.TuitorialApp(chapters, title_slide=None, initial_chapter=None, initial_step=0)[source]#

Bases: App

A Textual app for presenting code tutorials.

CSS: ClassVar[str] = '\n    Tabs {\n        dock: top;\n    }\n\n    TabPane {\n        padding: 1 1;\n    }\n\n    CodeDisplay {\n        height: auto;\n        margin: 1;\n        background: $surface;\n        color: $text;\n        border: solid $primary;\n        padding: 1;\n    }\n\n    #description {\n        height: auto;\n        margin: 1;\n        background: $surface-darken-1;\n        color: $text;\n        border: solid $primary;\n        padding: 1;\n    }\n\n    ContentContainer {\n        height: auto;\n    }\n\n    #image-container {\n        align: center middle;\n        height: auto;\n    }\n\n    #image {\n        width: auto;\n        height: auto;\n    }\n\n    #title-container {\n        align: center middle;\n    }\n\n    #title-rich-log {\n        overflow-y: auto;\n        background: black 0%;\n        width: auto;\n        height: auto;\n        /* When removing the border, the whole thing is gone? */\n        border: solid green 0%;\n    }\n\n    #markdown-container {\n        height: 1fr;\n    }\n\n    '#

Inline CSS, useful for quick scripts. This is loaded after CSS_PATH, and therefore takes priority in the event of a specificity clash.

BINDINGS: ClassVar[list[Binding]] = [Binding(key='q', action='quit', description='Quit', show=True, key_display=None, priority=False, tooltip='', id=None, system=False), Binding(key='down', action='next_focus', description='Next Focus', show=True, key_display=None, priority=False, tooltip='', id=None, system=False), Binding(key='up', action='previous_focus', description='Previous Focus', show=True, key_display=None, priority=False, tooltip='', id=None, system=False), Binding(key='d', action='toggle_dim', description='Toggle Dim', show=True, key_display=None, priority=False, tooltip='', id=None, system=False), ('r', 'reset_focus', 'Reset Focus')]#

The default key bindings.

compose()[source]#

Create child widgets for the app.

Return type:

Iterable[Widget]

async set_chapter(chapter_index, *, nearest=False)[source]#

Set the current chapter and update the display.

Return type:

None

async set_step(step_index)[source]#

Set the current step and update the display.

Return type:

None

async on_mount()[source]#

Set initial chapter and step.

Return type:

None

property current_chapter: Chapter#

Get the current chapter.

on_change(event)[source]#

Handle tab change event and set the current chapter index.

Return type:

None

current_tab_pane()[source]#

Get the current tab id.

Return type:

TabPane

async update_display()[source]#

Update the display with current focus.

Return type:

None

async action_next_focus()[source]#

Handle next focus action.

Return type:

None

async action_previous_focus()[source]#

Handle previous focus action.

Return type:

None

async action_reset_focus()[source]#

Reset to first focus pattern.

Return type:

None

async action_toggle_dim()[source]#

Toggle dim background.

Return type:

None

async next_focus_scroll()[source]#

Handle next focus scroll event.

Return type:

None

async previous_focus_scroll()[source]#

Handle previous focus scroll event.

Return type:

None