prezo 0.3.1__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.
- prezo/__init__.py +216 -0
- prezo/app.py +947 -0
- prezo/config.py +247 -0
- prezo/export.py +833 -0
- prezo/images/__init__.py +14 -0
- prezo/images/ascii.py +240 -0
- prezo/images/base.py +111 -0
- prezo/images/chafa.py +137 -0
- prezo/images/iterm.py +126 -0
- prezo/images/kitty.py +360 -0
- prezo/images/overlay.py +291 -0
- prezo/images/processor.py +139 -0
- prezo/images/sixel.py +180 -0
- prezo/parser.py +456 -0
- prezo/screens/__init__.py +21 -0
- prezo/screens/base.py +65 -0
- prezo/screens/blackout.py +60 -0
- prezo/screens/goto.py +99 -0
- prezo/screens/help.py +140 -0
- prezo/screens/overview.py +184 -0
- prezo/screens/search.py +252 -0
- prezo/screens/toc.py +254 -0
- prezo/terminal.py +147 -0
- prezo/themes.py +129 -0
- prezo/widgets/__init__.py +9 -0
- prezo/widgets/image_display.py +117 -0
- prezo/widgets/slide_button.py +72 -0
- prezo/widgets/status_bar.py +240 -0
- prezo-0.3.1.dist-info/METADATA +194 -0
- prezo-0.3.1.dist-info/RECORD +32 -0
- prezo-0.3.1.dist-info/WHEEL +4 -0
- prezo-0.3.1.dist-info/entry_points.txt +3 -0
prezo/__init__.py
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"""Prezo - TUI-based presentation tool."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
# ANSI color codes for error messages
|
|
10
|
+
RED = "\033[91m"
|
|
11
|
+
YELLOW = "\033[93m"
|
|
12
|
+
BOLD = "\033[1m"
|
|
13
|
+
RESET = "\033[0m"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _error(message: str) -> None:
|
|
17
|
+
"""Print an error message and exit."""
|
|
18
|
+
sys.stderr.write(f"{RED}{BOLD}error:{RESET} {message}\n")
|
|
19
|
+
sys.exit(1)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _warn(message: str) -> None:
|
|
23
|
+
"""Print a warning message."""
|
|
24
|
+
sys.stderr.write(f"{YELLOW}{BOLD}warning:{RESET} {message}\n")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _validate_file(path: Path, must_exist: bool = True) -> Path:
|
|
28
|
+
"""Validate a file path and return the resolved path.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
path: The path to validate.
|
|
32
|
+
must_exist: Whether the file must exist.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
The resolved absolute path.
|
|
36
|
+
|
|
37
|
+
"""
|
|
38
|
+
resolved = path.resolve()
|
|
39
|
+
|
|
40
|
+
if must_exist and not resolved.exists():
|
|
41
|
+
_error(
|
|
42
|
+
f"file not found: {path}\n\n"
|
|
43
|
+
"Make sure the file exists and the path is correct."
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
if must_exist and resolved.is_dir():
|
|
47
|
+
_error(
|
|
48
|
+
f"expected a file, got a directory: {path}\n\nProvide a path to a .md file."
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
if must_exist and resolved.suffix.lower() not in (".md", ".markdown"):
|
|
52
|
+
_warn(
|
|
53
|
+
f"file '{path.name}' does not have a .md extension "
|
|
54
|
+
"- treating as markdown anyway"
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
return resolved
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def main() -> None:
|
|
61
|
+
"""Entry point for Prezo."""
|
|
62
|
+
parser = argparse.ArgumentParser(
|
|
63
|
+
prog="prezo",
|
|
64
|
+
description="TUI-based presentation tool for Markdown slides",
|
|
65
|
+
epilog="For more information, visit: https://github.com/abilian/prezo",
|
|
66
|
+
)
|
|
67
|
+
parser.add_argument(
|
|
68
|
+
"file",
|
|
69
|
+
nargs="?",
|
|
70
|
+
help="Path to the presentation file (.md)",
|
|
71
|
+
)
|
|
72
|
+
parser.add_argument(
|
|
73
|
+
"--export",
|
|
74
|
+
"-e",
|
|
75
|
+
metavar="FORMAT",
|
|
76
|
+
choices=["pdf", "html", "png", "svg"],
|
|
77
|
+
help="Export presentation to format (pdf, html, png, svg)",
|
|
78
|
+
)
|
|
79
|
+
parser.add_argument(
|
|
80
|
+
"--slide",
|
|
81
|
+
metavar="NUM",
|
|
82
|
+
type=int,
|
|
83
|
+
help="Export only slide number NUM (1-indexed, for png/svg export)",
|
|
84
|
+
)
|
|
85
|
+
parser.add_argument(
|
|
86
|
+
"--output",
|
|
87
|
+
"-o",
|
|
88
|
+
metavar="PATH",
|
|
89
|
+
help="Output path for export (default: same name with new extension)",
|
|
90
|
+
)
|
|
91
|
+
parser.add_argument(
|
|
92
|
+
"--theme",
|
|
93
|
+
"-t",
|
|
94
|
+
metavar="NAME",
|
|
95
|
+
default="dark",
|
|
96
|
+
help="Theme for export (dark, light, dracula, solarized-dark, nord, gruvbox)",
|
|
97
|
+
)
|
|
98
|
+
parser.add_argument(
|
|
99
|
+
"--size",
|
|
100
|
+
"-s",
|
|
101
|
+
metavar="WxH",
|
|
102
|
+
default="80x24",
|
|
103
|
+
help="Screen size for export as WIDTHxHEIGHT (default: 80x24)",
|
|
104
|
+
)
|
|
105
|
+
parser.add_argument(
|
|
106
|
+
"--no-chrome",
|
|
107
|
+
action="store_true",
|
|
108
|
+
help="Export without window decorations (for printing)",
|
|
109
|
+
)
|
|
110
|
+
parser.add_argument(
|
|
111
|
+
"--scale",
|
|
112
|
+
metavar="FACTOR",
|
|
113
|
+
type=float,
|
|
114
|
+
default=2.0,
|
|
115
|
+
help="Scale factor for PNG export (default: 2.0 for higher resolution)",
|
|
116
|
+
)
|
|
117
|
+
parser.add_argument(
|
|
118
|
+
"--no-watch",
|
|
119
|
+
action="store_true",
|
|
120
|
+
help="Disable file watching for auto-reload",
|
|
121
|
+
)
|
|
122
|
+
parser.add_argument(
|
|
123
|
+
"--config",
|
|
124
|
+
"-c",
|
|
125
|
+
metavar="PATH",
|
|
126
|
+
help="Path to custom config file (default: ~/.config/prezo/config.toml)",
|
|
127
|
+
)
|
|
128
|
+
parser.add_argument(
|
|
129
|
+
"--image-mode",
|
|
130
|
+
metavar="MODE",
|
|
131
|
+
choices=["auto", "kitty", "sixel", "iterm", "ascii", "none"],
|
|
132
|
+
help="Image rendering mode (auto, kitty, sixel, iterm, ascii, none)",
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
args = parser.parse_args()
|
|
136
|
+
|
|
137
|
+
# Load config from custom path or default
|
|
138
|
+
from .config import load_config # noqa: PLC0415
|
|
139
|
+
|
|
140
|
+
config_path = Path(args.config) if args.config else None
|
|
141
|
+
if config_path and not config_path.exists():
|
|
142
|
+
_error(f"config file not found: {config_path}")
|
|
143
|
+
|
|
144
|
+
config = load_config(config_path)
|
|
145
|
+
|
|
146
|
+
# Override image mode if specified
|
|
147
|
+
if args.image_mode:
|
|
148
|
+
config.images.mode = args.image_mode
|
|
149
|
+
|
|
150
|
+
if args.export:
|
|
151
|
+
if not args.file:
|
|
152
|
+
_error(
|
|
153
|
+
"--export requires a presentation file\n\n"
|
|
154
|
+
"Usage: prezo -e pdf presentation.md"
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
# Validate source file
|
|
158
|
+
source_path = _validate_file(Path(args.file))
|
|
159
|
+
|
|
160
|
+
# Parse size
|
|
161
|
+
try:
|
|
162
|
+
width, height = map(int, args.size.lower().split("x"))
|
|
163
|
+
except ValueError:
|
|
164
|
+
_error(
|
|
165
|
+
f"invalid size format: {args.size}\n\n"
|
|
166
|
+
"Use WIDTHxHEIGHT format (e.g., 80x24, 100x30)"
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
if args.export == "html":
|
|
170
|
+
from .export import run_html_export # noqa: PLC0415
|
|
171
|
+
|
|
172
|
+
sys.exit(
|
|
173
|
+
run_html_export(
|
|
174
|
+
str(source_path),
|
|
175
|
+
args.output,
|
|
176
|
+
theme=args.theme,
|
|
177
|
+
),
|
|
178
|
+
)
|
|
179
|
+
elif args.export in ("png", "svg"):
|
|
180
|
+
from .export import run_image_export # noqa: PLC0415
|
|
181
|
+
|
|
182
|
+
sys.exit(
|
|
183
|
+
run_image_export(
|
|
184
|
+
str(source_path),
|
|
185
|
+
args.output,
|
|
186
|
+
output_format=args.export,
|
|
187
|
+
theme=args.theme,
|
|
188
|
+
width=width,
|
|
189
|
+
height=height,
|
|
190
|
+
chrome=not args.no_chrome,
|
|
191
|
+
slide_num=args.slide,
|
|
192
|
+
scale=args.scale,
|
|
193
|
+
),
|
|
194
|
+
)
|
|
195
|
+
else:
|
|
196
|
+
from .export import run_export # noqa: PLC0415
|
|
197
|
+
|
|
198
|
+
sys.exit(
|
|
199
|
+
run_export(
|
|
200
|
+
str(source_path),
|
|
201
|
+
args.output,
|
|
202
|
+
theme=args.theme,
|
|
203
|
+
width=width,
|
|
204
|
+
height=height,
|
|
205
|
+
chrome=not args.no_chrome,
|
|
206
|
+
),
|
|
207
|
+
)
|
|
208
|
+
else:
|
|
209
|
+
from .app import run_app # noqa: PLC0415
|
|
210
|
+
|
|
211
|
+
# Validate file if provided
|
|
212
|
+
file_path = None
|
|
213
|
+
if args.file:
|
|
214
|
+
file_path = _validate_file(Path(args.file))
|
|
215
|
+
|
|
216
|
+
run_app(file_path, watch=not args.no_watch, config=config)
|