Create beautiful terminal-based code tutorials with syntax highlighting and interactive navigation.
[ToC] 📚
- 🎨 Rich syntax highlighting with customizable styles
- 🔍 Multiple focus types: literal, regex, line, range, startswith, and between.
- ⌨️ Interactive keyboard navigation
- 📝 Step-by-step tutorial presentations
- 🖼️ Beautiful TUI using Textual
pip install tuitorial
Python
from tuitorial import Chapter, Step, TutorialApp, Focus
from rich.style import Style
# Your code to present
code = '''
def hello(name: str) -> str:
return f"Hello, {name}!"
def main():
print(hello("World"))
'''
# Define tutorial steps
steps = [
Step(
"Function Definition",
[Focus.regex(r"def hello.*:$", style="bold yellow")]
),
Step(
"Return Statement",
[Focus.literal('return f"Hello, {name}!"', style="bold green")]
),
Step(
"Main Function",
[Focus.range(code.find("def main"), len(code), style="bold blue")]
),
]
# Create a chapter
chapter = Chapter("Basic Example", code, steps)
# Run the tutorial
app = TutorialApp([chapter])
app.run()
YAML
chapters:
- title: "Basic Example"
code: |
def hello(name: str) -> str:
return f"Hello, {name}!"
def main():
print(hello("World"))
steps:
- description: "Function Definition"
focus:
- type: regex
pattern: "def hello.*:$"
style: "bold yellow"
- description: "Return Statement"
focus:
- type: literal
pattern: 'return f"Hello, {name}!"'
style: "bold green"
- description: "Main Function"
focus:
- type: range
start: 26 # Calculated index for "def main"
end: 53 # Calculated length of the code
style: "bold blue"
To run the YAML example:
- Save the YAML content as a
.yaml
file (e.g.,tutorial.yaml
). - Either:
- Use the provided
tuitorial.run_from_yaml
function: - Run
tuitorial tutorial.yaml
from the command line.
- Use the provided
# In a separate Python file (e.g., run_yaml.py)
from parse_yaml import run_from_yaml # Assuming parse_yaml.py is where you have the YAML parsing code
run_from_yaml("tutorial.yaml")
Python
# First chapter
chapter1_code = '''
def greet(name: str) -> str:
return f"Hello, {name}!"
'''
chapter1_steps = [
Step("Greeting Function", [Focus.regex(r"def greet.*:$")]),
Step("Return Statement", [Focus.literal('return f"Hello, {name}!"')]),
]
chapter1 = Chapter("Greetings", chapter1_code, chapter1_steps)
# Second chapter
chapter2_code = '''
def farewell(name: str) -> str:
return f"Goodbye, {name}!"
'''
chapter2_steps = [
Step("Farewell Function", [Focus.regex(r"def farewell.*:$")]),
Step("Return Statement", [Focus.literal('return f"Goodbye, {name}!"')]),
]
chapter2 = Chapter("Farewells", chapter2_code, chapter2_steps)
# Run tutorial with multiple chapters
app = TutorialApp([chapter1, chapter2])
app.run()
YAML
chapters:
- title: "Greetings"
code: |
def greet(name: str) -> str:
return f"Hello, {name}!"
steps:
- description: "Greeting Function"
focus:
- type: regex
pattern: "def greet.*:$"
- description: "Return Statement"
focus:
- type: literal
pattern: 'return f"Hello, {name}!"'
- title: "Farewells"
code: |
def farewell(name: str) -> str:
return f"Goodbye, {name}!"
steps:
- description: "Farewell Function"
focus:
- type: regex
pattern: "def farewell.*:$"
- description: "Return Statement"
focus:
- type: literal
pattern: 'return f"Goodbye, {name}!"'
Each step in a tutorial consists of a description and a list of focuses.
Step(
"Step Description", # Shown in the UI
[
Focus.literal("some text"), # One or more Focus objects
Focus.regex(r"pattern.*"), # Can combine different focus types
]
)
steps:
- description: "Step Description"
focus:
- type: literal
pattern: "some text"
- type: regex
pattern: "pattern.*"
Focus.literal("def", style="bold yellow")
focus:
- type: literal
pattern: "def"
style: "bold yellow"
Focus.regex(r"def \w+\(.*\):", style="bold green")
focus:
- type: regex
pattern: "def \\w+\\(.*\\):"
style: "bold green"
Focus.line(1, style="bold blue") # Highlight first line
focus:
- type: line
pattern: 1
style: "bold blue"
Focus.range(0, 10, style="bold magenta") # Highlight first 10 characters
focus:
- type: range
start: 0
end: 10
style: "bold magenta"
Highlights lines starting with the specified text. Can be configured to match from the start of any line or only at the start of the line.
Focus.startswith("import", style="bold blue", from_start_of_line=True)
Focus.startswith("from", style="bold blue", from_start_of_line=False)
focus:
- type: startswith
pattern: "import"
style: "bold blue"
from_start_of_line: true
- type: startswith
pattern: "from"
style: "bold blue"
from_start_of_line: false
Highlights text between two specified patterns. Supports inclusive or exclusive bounds, multiline matching, and greedy or non-greedy matching.
Focus.between("start_function", "end_function", style="bold blue", inclusive=True, multiline=True)
focus:
- type: between
start_pattern: "start_function"
end_pattern: "end_function"
style: "bold blue"
inclusive: true
multiline: true
Styles can be customized using Rich's style syntax:
from rich.style import Style
# Using string syntax
Focus.literal("def", style="bold yellow")
# Using Style object
Focus.literal("def", style=Style(bold=True, color="yellow"))
focus:
- type: literal
pattern: "def"
style: "bold yellow" # Using string syntax
- type: literal
pattern: "def"
style: "bold color(yellow)" # Using Style object
→
Next step in current chapter←
Previous step in current chaptertab
Next chaptershift+tab
Previous chapterr
Reset to first step of current chapterd
Toggle dim/bright backgroundq
Quit tutorial
Python
from tuitorial import TutorialApp, Focus
from rich.style import Style
# Define custom styles
FUNCTION_STYLE = Style(color="bright_yellow", bold=True)
ARGUMENT_STYLE = Style(color="bright_green", italic=True)
# Your code to present
code = '''
def hello(name: str) -> str:
return f"Hello, {name}!"
'''
# Create focused patterns
patterns = [
Focus.regex(r"def \w+", style=FUNCTION_STYLE),
Focus.regex(r"\([^)]*\)", style=ARGUMENT_STYLE),
]
# Create tutorial step
tutorial_steps = [
Step("Function Definition", patterns),
]
# Create a chapter
chapter = Chapter("Custom Patterns", code, tutorial_steps)
# Run the tutorial
app = TutorialApp([chapter])
app.run()
YAML
chapters:
- title: "Custom Patterns"
code: |
def hello(name: str) -> str:
return f"Hello, {name}!"
steps:
- description: "Function Definition"
focus:
- type: regex
pattern: "def \\w+"
style: "bright_yellow bold"
- type: regex
pattern: "\\([^)]*\\)"
style: "bright_green italic"
Python
from tuitorial import Chapter, Step, TutorialApp, Focus
from rich.style import Style
# Your code to present
code = '''
def hello(name: str) -> str:
return f"Hello, {name}!"
'''
tutorial_steps = [
Step(
"Input/Output",
[
Focus.literal("name", style="bold cyan"),
Focus.regex(r"->.*$", style="bold yellow"),
]
),
]
# Create a chapter
chapter = Chapter("Multiple Highlights", code, tutorial_steps)
# Run the tutorial
app = TutorialApp([chapter])
app.run()
YAML
chapters:
- title: "Multiple Highlights"
code: |
def hello(name: str) -> str:
return f"Hello, {name}!"
steps:
- description: "Input/Output"
focus:
- type: literal
pattern: "name"
style: "bold cyan"
- type: regex
pattern: "->.*$"
style: "bold yellow"
This helper function simplifies the creation of chapters that present information in a bullet-point format. Each step in the generated chapter will highlight a different bullet point.
Python
from rich.style import Style
from tuitorial import TutorialApp
from tuitorial.helpers import create_bullet_point_chapter
bullet_points = [
"This is the first point.",
"Here is the second point.",
"And finally, the third point.",
]
# Create a chapter with bullet points
bullet_point_chapter = create_bullet_point_chapter(
"My Bullet Points",
bullet_points,
style=Style(color="magenta", bold=True),
)
# You can also add extra descriptive text per step:
bullet_point_chapter_with_extras = create_bullet_point_chapter(
"My Bullet Points with Extras",
bullet_points,
extras=[
"Extra info for point 1.",
"More details about point 2.",
"Final thoughts on point 3.",
],
style=Style(color="green", bold=True),
)
app = TutorialApp([bullet_point_chapter, bullet_point_chapter_with_extras])
app.run()
YAML
chapters:
- title: "My Bullet Points"
type: bullet_points
bullet_points:
- "This is the first point."
- "Here is the second point."
- "And finally, the third point."
style: "magenta bold"
- title: "My Bullet Points with Extras"
type: bullet_points
bullet_points:
- "This is the first point."
- "Here is the second point."
- "And finally, the third point."
extras:
- "Extra info for point 1."
- "More details about point 2."
- "Final thoughts on point 3."
style: "green bold"
-
Clone the repository:
git clone https://github.com/basnijholt/tuitorial.git cd tuitorial
-
Create a virtual environment:
python -m venv venv source venv/bin/activate # or `venv\Scripts\activate` on Windows
-
Install development dependencies:
pip install -e ".[test]"
-
Run tests:
pytest
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature
) - Commit your changes (
git commit -m 'Add some AmazingFeature'
) - Push to the branch (
git push origin feature/AmazingFeature
) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
Q: The colors don't show up correctly in my terminal. A: Make sure your terminal supports true color and has a compatible color scheme.
Q: The tutorial doesn't respond to keyboard input. A: Verify that your terminal emulator is properly forwarding keyboard events.