prezo 2026.1.2__py3-none-any.whl → 2026.1.3__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 +8 -0
- prezo/app.py +19 -27
- prezo/config.py +1 -1
- prezo/export/__init__.py +36 -0
- prezo/export/common.py +77 -0
- prezo/export/html.py +340 -0
- prezo/export/images.py +261 -0
- prezo/export/pdf.py +497 -0
- prezo/export/svg.py +170 -0
- prezo/layout.py +268 -52
- {prezo-2026.1.2.dist-info → prezo-2026.1.3.dist-info}/METADATA +1 -1
- {prezo-2026.1.2.dist-info → prezo-2026.1.3.dist-info}/RECORD +14 -9
- prezo/export.py +0 -860
- {prezo-2026.1.2.dist-info → prezo-2026.1.3.dist-info}/WHEEL +0 -0
- {prezo-2026.1.2.dist-info → prezo-2026.1.3.dist-info}/entry_points.txt +0 -0
prezo/layout.py
CHANGED
|
@@ -11,6 +11,13 @@ Supports Pandoc-style fenced div syntax:
|
|
|
11
11
|
:::
|
|
12
12
|
:::
|
|
13
13
|
|
|
14
|
+
Additional layout blocks:
|
|
15
|
+
::: center - Horizontally centered content
|
|
16
|
+
::: right - Right-aligned content
|
|
17
|
+
::: spacer [n] - Vertical space (default 1 line)
|
|
18
|
+
::: box [title] - Bordered panel with optional title
|
|
19
|
+
::: divider [style] - Horizontal rule (single/double/thick/dashed)
|
|
20
|
+
|
|
14
21
|
"""
|
|
15
22
|
|
|
16
23
|
from __future__ import annotations
|
|
@@ -20,9 +27,12 @@ from dataclasses import dataclass, field
|
|
|
20
27
|
from io import StringIO
|
|
21
28
|
from typing import TYPE_CHECKING, Literal
|
|
22
29
|
|
|
30
|
+
from rich.cells import cell_len
|
|
23
31
|
from rich.console import Console, ConsoleOptions, Group, RenderResult
|
|
24
32
|
from rich.markdown import Markdown
|
|
25
33
|
from rich.measure import Measurement
|
|
34
|
+
from rich.panel import Panel
|
|
35
|
+
from rich.rule import Rule
|
|
26
36
|
from rich.text import Text
|
|
27
37
|
|
|
28
38
|
if TYPE_CHECKING:
|
|
@@ -33,22 +43,30 @@ if TYPE_CHECKING:
|
|
|
33
43
|
# -----------------------------------------------------------------------------
|
|
34
44
|
|
|
35
45
|
|
|
46
|
+
BlockType = Literal[
|
|
47
|
+
"plain", "columns", "column", "center", "right", "spacer", "box", "divider"
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
|
|
36
51
|
@dataclass
|
|
37
52
|
class LayoutBlock:
|
|
38
53
|
"""A block of content with layout information."""
|
|
39
54
|
|
|
40
|
-
type:
|
|
55
|
+
type: BlockType
|
|
41
56
|
content: str = "" # Raw markdown content (for leaf blocks)
|
|
42
57
|
children: list[LayoutBlock] = field(default_factory=list)
|
|
43
58
|
width_percent: int = 0 # For column blocks (0 = auto/equal)
|
|
59
|
+
title: str = "" # For box blocks
|
|
60
|
+
style: str = "" # For divider blocks (single/double/thick/dashed)
|
|
44
61
|
|
|
45
62
|
|
|
46
63
|
# -----------------------------------------------------------------------------
|
|
47
64
|
# Parser
|
|
48
65
|
# -----------------------------------------------------------------------------
|
|
49
66
|
|
|
50
|
-
# Pattern for opening fenced div: ::: type [
|
|
51
|
-
|
|
67
|
+
# Pattern for opening fenced div: ::: type [arg]
|
|
68
|
+
# arg can be: a number (width %), a quoted string (title), or a word (style)
|
|
69
|
+
OPEN_PATTERN = re.compile(r'^:::\s*(\w+)(?:\s+"([^"]+)"|\s+(\S+))?\s*$')
|
|
52
70
|
# Pattern for closing fenced div: :::
|
|
53
71
|
CLOSE_PATTERN = re.compile(r"^:::\s*$")
|
|
54
72
|
|
|
@@ -76,10 +94,14 @@ def parse_layout(content: str) -> list[LayoutBlock]:
|
|
|
76
94
|
|
|
77
95
|
if match:
|
|
78
96
|
block_type = match.group(1).lower()
|
|
79
|
-
|
|
97
|
+
# Group 2 is quoted string, Group 3 is unquoted arg
|
|
98
|
+
quoted_arg = match.group(2) # For "title"
|
|
99
|
+
unquoted_arg = match.group(3) # For width or style
|
|
80
100
|
|
|
81
101
|
# Find matching close and nested content
|
|
82
|
-
block, end_idx = _parse_fenced_block(
|
|
102
|
+
block, end_idx = _parse_fenced_block(
|
|
103
|
+
lines, i, block_type, quoted_arg, unquoted_arg
|
|
104
|
+
)
|
|
83
105
|
if block:
|
|
84
106
|
blocks.append(block)
|
|
85
107
|
i = end_idx + 1
|
|
@@ -104,8 +126,59 @@ def parse_layout(content: str) -> list[LayoutBlock]:
|
|
|
104
126
|
return blocks
|
|
105
127
|
|
|
106
128
|
|
|
129
|
+
def _create_block(
|
|
130
|
+
block_type: str,
|
|
131
|
+
inner_content: str,
|
|
132
|
+
quoted_arg: str | None,
|
|
133
|
+
unquoted_arg: str | None,
|
|
134
|
+
) -> LayoutBlock:
|
|
135
|
+
"""Create a LayoutBlock from parsed fenced div content.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
block_type: The type from ::: type.
|
|
139
|
+
inner_content: Content inside the fenced div.
|
|
140
|
+
quoted_arg: Quoted argument (e.g., title for box).
|
|
141
|
+
unquoted_arg: Unquoted argument (e.g., width or style).
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
A LayoutBlock of the appropriate type.
|
|
145
|
+
|
|
146
|
+
"""
|
|
147
|
+
content = inner_content.strip()
|
|
148
|
+
width = int(unquoted_arg) if unquoted_arg and unquoted_arg.isdigit() else 0
|
|
149
|
+
|
|
150
|
+
# Use a dispatch table for simple content blocks
|
|
151
|
+
simple_types = {"center", "right", "plain"}
|
|
152
|
+
|
|
153
|
+
if block_type == "columns":
|
|
154
|
+
block = LayoutBlock(type="columns")
|
|
155
|
+
block.children = parse_layout(inner_content)
|
|
156
|
+
elif block_type == "column":
|
|
157
|
+
block = LayoutBlock(type="column", content=content, width_percent=width)
|
|
158
|
+
elif block_type == "spacer":
|
|
159
|
+
lines_count = width if width > 0 else 1
|
|
160
|
+
block = LayoutBlock(type="spacer", width_percent=lines_count)
|
|
161
|
+
elif block_type == "box":
|
|
162
|
+
title = quoted_arg or unquoted_arg or ""
|
|
163
|
+
block = LayoutBlock(type="box", content=content, title=title)
|
|
164
|
+
elif block_type == "divider":
|
|
165
|
+
style = unquoted_arg if unquoted_arg else "single"
|
|
166
|
+
block = LayoutBlock(type="divider", style=style)
|
|
167
|
+
elif block_type in simple_types:
|
|
168
|
+
block = LayoutBlock(type=block_type, content=content)
|
|
169
|
+
else:
|
|
170
|
+
# Unknown block type - treat as plain
|
|
171
|
+
block = LayoutBlock(type="plain", content=content)
|
|
172
|
+
|
|
173
|
+
return block
|
|
174
|
+
|
|
175
|
+
|
|
107
176
|
def _parse_fenced_block(
|
|
108
|
-
lines: list[str],
|
|
177
|
+
lines: list[str],
|
|
178
|
+
start: int,
|
|
179
|
+
block_type: str,
|
|
180
|
+
quoted_arg: str | None,
|
|
181
|
+
unquoted_arg: str | None,
|
|
109
182
|
) -> tuple[LayoutBlock | None, int]:
|
|
110
183
|
"""Parse a fenced div block starting at the given line.
|
|
111
184
|
|
|
@@ -113,7 +186,8 @@ def _parse_fenced_block(
|
|
|
113
186
|
lines: All lines of content.
|
|
114
187
|
start: Starting line index (the opening :::).
|
|
115
188
|
block_type: The type from ::: type.
|
|
116
|
-
|
|
189
|
+
quoted_arg: Quoted argument (e.g., title for box).
|
|
190
|
+
unquoted_arg: Unquoted argument (e.g., width or style).
|
|
117
191
|
|
|
118
192
|
Returns:
|
|
119
193
|
Tuple of (LayoutBlock or None, end line index).
|
|
@@ -143,28 +217,8 @@ def _parse_fenced_block(
|
|
|
143
217
|
return None, start
|
|
144
218
|
|
|
145
219
|
inner_content = "\n".join(content_lines)
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
if block_type in ("columns", "column", "center"):
|
|
149
|
-
block = LayoutBlock(
|
|
150
|
-
type=block_type, # type: ignore[arg-type]
|
|
151
|
-
width_percent=width,
|
|
152
|
-
)
|
|
153
|
-
|
|
154
|
-
if block_type == "columns":
|
|
155
|
-
# Parse children (should be column blocks)
|
|
156
|
-
block.children = parse_layout(inner_content)
|
|
157
|
-
elif block_type == "column":
|
|
158
|
-
# Column contains markdown content, but might have nested structure
|
|
159
|
-
# For now, treat as plain content
|
|
160
|
-
block.content = inner_content.strip()
|
|
161
|
-
elif block_type == "center":
|
|
162
|
-
block.content = inner_content.strip()
|
|
163
|
-
|
|
164
|
-
return block, i
|
|
165
|
-
|
|
166
|
-
# Unknown block type - treat content as plain
|
|
167
|
-
return LayoutBlock(type="plain", content=inner_content.strip()), i
|
|
220
|
+
block = _create_block(block_type, inner_content, quoted_arg, unquoted_arg)
|
|
221
|
+
return block, i
|
|
168
222
|
|
|
169
223
|
|
|
170
224
|
def has_layout_blocks(content: str) -> bool:
|
|
@@ -303,10 +357,17 @@ class ColumnsRenderable:
|
|
|
303
357
|
file=StringIO(),
|
|
304
358
|
)
|
|
305
359
|
|
|
306
|
-
# Render
|
|
360
|
+
# Render content - check for nested layout blocks
|
|
307
361
|
if column.content:
|
|
308
|
-
|
|
309
|
-
|
|
362
|
+
if has_layout_blocks(column.content):
|
|
363
|
+
# Parse and render nested layout blocks
|
|
364
|
+
blocks = parse_layout(column.content)
|
|
365
|
+
renderable = render_layout(blocks)
|
|
366
|
+
col_console.print(renderable)
|
|
367
|
+
else:
|
|
368
|
+
# Plain markdown
|
|
369
|
+
md = Markdown(column.content)
|
|
370
|
+
col_console.print(md)
|
|
310
371
|
|
|
311
372
|
# Get rendered lines
|
|
312
373
|
output = col_console.export_text(styles=True)
|
|
@@ -405,6 +466,170 @@ class CenterRenderable:
|
|
|
405
466
|
return Measurement(1, options.max_width)
|
|
406
467
|
|
|
407
468
|
|
|
469
|
+
class RightRenderable:
|
|
470
|
+
"""Rich renderable that right-aligns content."""
|
|
471
|
+
|
|
472
|
+
def __init__(self, content: str) -> None:
|
|
473
|
+
"""Initialize right-align renderable.
|
|
474
|
+
|
|
475
|
+
Args:
|
|
476
|
+
content: Markdown content to right-align.
|
|
477
|
+
|
|
478
|
+
"""
|
|
479
|
+
self.content = content
|
|
480
|
+
|
|
481
|
+
def __rich_console__(
|
|
482
|
+
self, console: Console, options: ConsoleOptions
|
|
483
|
+
) -> RenderResult:
|
|
484
|
+
"""Render right-aligned content."""
|
|
485
|
+
yield Text("")
|
|
486
|
+
md = Markdown(self.content, justify="right")
|
|
487
|
+
yield md
|
|
488
|
+
yield Text("")
|
|
489
|
+
|
|
490
|
+
def __rich_measure__(
|
|
491
|
+
self, console: Console, options: ConsoleOptions
|
|
492
|
+
) -> Measurement:
|
|
493
|
+
"""Return the measurement of this renderable."""
|
|
494
|
+
return Measurement(1, options.max_width)
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
class SpacerRenderable:
|
|
498
|
+
"""Rich renderable that creates vertical space."""
|
|
499
|
+
|
|
500
|
+
def __init__(self, lines: int = 1) -> None:
|
|
501
|
+
"""Initialize spacer renderable.
|
|
502
|
+
|
|
503
|
+
Args:
|
|
504
|
+
lines: Number of blank lines to insert.
|
|
505
|
+
|
|
506
|
+
"""
|
|
507
|
+
self.lines = max(1, lines)
|
|
508
|
+
|
|
509
|
+
def __rich_console__(
|
|
510
|
+
self, console: Console, options: ConsoleOptions
|
|
511
|
+
) -> RenderResult:
|
|
512
|
+
"""Render vertical space."""
|
|
513
|
+
for _ in range(self.lines):
|
|
514
|
+
yield Text("")
|
|
515
|
+
|
|
516
|
+
def __rich_measure__(
|
|
517
|
+
self, console: Console, options: ConsoleOptions
|
|
518
|
+
) -> Measurement:
|
|
519
|
+
"""Return the measurement of this renderable."""
|
|
520
|
+
return Measurement(0, 0)
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
class BoxRenderable:
|
|
524
|
+
"""Rich renderable that displays content in a bordered panel."""
|
|
525
|
+
|
|
526
|
+
def __init__(self, content: str, title: str = "") -> None:
|
|
527
|
+
"""Initialize box renderable.
|
|
528
|
+
|
|
529
|
+
Args:
|
|
530
|
+
content: Markdown content to display in the box.
|
|
531
|
+
title: Optional title for the box.
|
|
532
|
+
|
|
533
|
+
"""
|
|
534
|
+
self.content = content
|
|
535
|
+
self.title = title
|
|
536
|
+
|
|
537
|
+
def __rich_console__(
|
|
538
|
+
self, console: Console, options: ConsoleOptions
|
|
539
|
+
) -> RenderResult:
|
|
540
|
+
"""Render content in a bordered panel."""
|
|
541
|
+
yield Text("")
|
|
542
|
+
md = Markdown(self.content)
|
|
543
|
+
panel = Panel(md, title=self.title if self.title else None)
|
|
544
|
+
yield panel
|
|
545
|
+
yield Text("")
|
|
546
|
+
|
|
547
|
+
def __rich_measure__(
|
|
548
|
+
self, console: Console, options: ConsoleOptions
|
|
549
|
+
) -> Measurement:
|
|
550
|
+
"""Return the measurement of this renderable."""
|
|
551
|
+
return Measurement(1, options.max_width)
|
|
552
|
+
|
|
553
|
+
|
|
554
|
+
# Divider style characters
|
|
555
|
+
DIVIDER_STYLES = {
|
|
556
|
+
"single": "─",
|
|
557
|
+
"double": "═",
|
|
558
|
+
"thick": "━",
|
|
559
|
+
"dashed": "╌",
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
|
|
563
|
+
class DividerRenderable:
|
|
564
|
+
"""Rich renderable that displays a horizontal rule."""
|
|
565
|
+
|
|
566
|
+
def __init__(self, style: str = "single") -> None:
|
|
567
|
+
"""Initialize divider renderable.
|
|
568
|
+
|
|
569
|
+
Args:
|
|
570
|
+
style: Style of the divider (single, double, thick, dashed).
|
|
571
|
+
|
|
572
|
+
"""
|
|
573
|
+
self.style = style if style in DIVIDER_STYLES else "single"
|
|
574
|
+
|
|
575
|
+
def __rich_console__(
|
|
576
|
+
self, console: Console, options: ConsoleOptions
|
|
577
|
+
) -> RenderResult:
|
|
578
|
+
"""Render horizontal rule."""
|
|
579
|
+
yield Text("")
|
|
580
|
+
char = DIVIDER_STYLES[self.style]
|
|
581
|
+
yield Rule(characters=char)
|
|
582
|
+
yield Text("")
|
|
583
|
+
|
|
584
|
+
def __rich_measure__(
|
|
585
|
+
self, console: Console, options: ConsoleOptions
|
|
586
|
+
) -> Measurement:
|
|
587
|
+
"""Return the measurement of this renderable."""
|
|
588
|
+
return Measurement(1, options.max_width)
|
|
589
|
+
|
|
590
|
+
|
|
591
|
+
def _render_block(block: LayoutBlock) -> list[RenderableType]:
|
|
592
|
+
"""Render a single block to Rich renderables.
|
|
593
|
+
|
|
594
|
+
Args:
|
|
595
|
+
block: A LayoutBlock to render.
|
|
596
|
+
|
|
597
|
+
Returns:
|
|
598
|
+
List of Rich renderables for this block.
|
|
599
|
+
|
|
600
|
+
"""
|
|
601
|
+
if block.type == "columns":
|
|
602
|
+
result: list[RenderableType] = []
|
|
603
|
+
columns = [c for c in block.children if c.type == "column"]
|
|
604
|
+
if columns:
|
|
605
|
+
result.append(ColumnsRenderable(columns))
|
|
606
|
+
# Also render any non-column children (plain text between columns)
|
|
607
|
+
for child in block.children:
|
|
608
|
+
if child.type == "plain":
|
|
609
|
+
result.append(Markdown(child.content))
|
|
610
|
+
return result
|
|
611
|
+
|
|
612
|
+
if block.type == "spacer":
|
|
613
|
+
return [SpacerRenderable(block.width_percent)]
|
|
614
|
+
|
|
615
|
+
if block.type == "box":
|
|
616
|
+
return [BoxRenderable(block.content, block.title)]
|
|
617
|
+
|
|
618
|
+
if block.type == "divider":
|
|
619
|
+
return [DividerRenderable(block.style)]
|
|
620
|
+
|
|
621
|
+
# Simple content blocks: plain, center, right, column
|
|
622
|
+
renderable_map: dict[str, type] = {
|
|
623
|
+
"center": CenterRenderable,
|
|
624
|
+
"right": RightRenderable,
|
|
625
|
+
}
|
|
626
|
+
if block.type in renderable_map:
|
|
627
|
+
return [renderable_map[block.type](block.content)]
|
|
628
|
+
|
|
629
|
+
# Default: plain markdown (for "plain" and standalone "column")
|
|
630
|
+
return [Markdown(block.content)]
|
|
631
|
+
|
|
632
|
+
|
|
408
633
|
def render_layout(
|
|
409
634
|
blocks: list[LayoutBlock],
|
|
410
635
|
) -> RenderableType:
|
|
@@ -420,23 +645,7 @@ def render_layout(
|
|
|
420
645
|
renderables: list[RenderableType] = []
|
|
421
646
|
|
|
422
647
|
for block in blocks:
|
|
423
|
-
|
|
424
|
-
case "plain":
|
|
425
|
-
renderables.append(Markdown(block.content))
|
|
426
|
-
case "columns":
|
|
427
|
-
# Filter to only column children
|
|
428
|
-
columns = [c for c in block.children if c.type == "column"]
|
|
429
|
-
if columns:
|
|
430
|
-
renderables.append(ColumnsRenderable(columns))
|
|
431
|
-
# Also render any non-column children (plain text between columns)
|
|
432
|
-
for child in block.children:
|
|
433
|
-
if child.type == "plain":
|
|
434
|
-
renderables.append(Markdown(child.content))
|
|
435
|
-
case "center":
|
|
436
|
-
renderables.append(CenterRenderable(block.content))
|
|
437
|
-
case "column":
|
|
438
|
-
# Standalone column (shouldn't happen normally)
|
|
439
|
-
renderables.append(Markdown(block.content))
|
|
648
|
+
renderables.extend(_render_block(block))
|
|
440
649
|
|
|
441
650
|
if len(renderables) == 1:
|
|
442
651
|
return renderables[0]
|
|
@@ -452,13 +661,20 @@ _ANSI_PATTERN = re.compile(r"\x1b\[[0-9;]*m")
|
|
|
452
661
|
|
|
453
662
|
|
|
454
663
|
def _visible_length(text: str) -> int:
|
|
455
|
-
"""Calculate visible
|
|
664
|
+
"""Calculate visible cell width of text, excluding ANSI codes.
|
|
665
|
+
|
|
666
|
+
Uses Rich's cell_len for proper Unicode width handling:
|
|
667
|
+
- Regular ASCII characters = 1 cell
|
|
668
|
+
- Wide characters (CJK, emoji) = 2 cells
|
|
669
|
+
- Zero-width characters = 0 cells
|
|
456
670
|
|
|
457
671
|
Args:
|
|
458
672
|
text: Text possibly containing ANSI escape codes.
|
|
459
673
|
|
|
460
674
|
Returns:
|
|
461
|
-
Visible
|
|
675
|
+
Visible cell width (terminal columns).
|
|
462
676
|
|
|
463
677
|
"""
|
|
464
|
-
|
|
678
|
+
# Strip ANSI codes first, then calculate cell width
|
|
679
|
+
clean_text = _ANSI_PATTERN.sub("", text)
|
|
680
|
+
return cell_len(clean_text)
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
prezo/__init__.py,sha256=
|
|
2
|
-
prezo/app.py,sha256=
|
|
3
|
-
prezo/config.py,sha256=
|
|
4
|
-
prezo/export.py,sha256=
|
|
1
|
+
prezo/__init__.py,sha256=RDpFh0F3DGdMB08n7G3HM-c14JAoqvElq4DyXLSPDQg,6740
|
|
2
|
+
prezo/app.py,sha256=3RPSx56hjyyG55ueNWHvuUNe-KkQ3ZzidNgBRc3I2WQ,31713
|
|
3
|
+
prezo/config.py,sha256=DLHUQkThxhmYXQE1AgpWkPvtNlDwOxQRSNjRrfJJEew,6646
|
|
4
|
+
prezo/export/__init__.py,sha256=jdf4Xu71aUKPBXUt8k8TEUzgMee4bfEh71iJGZXtVKE,1010
|
|
5
|
+
prezo/export/common.py,sha256=W9Gn2_wjd3EPuenAECrs6pqsJEWFBJHtGs8y6-4VEKQ,2451
|
|
6
|
+
prezo/export/html.py,sha256=GLRjTvZUEmvee6F2NlU3sn94H47PqLTLpaC3Ab_kxo8,8787
|
|
7
|
+
prezo/export/images.py,sha256=TNTGIYNRTxwVjbHqZXYjIj0gouV_Dy1_DtenMlxsquE,7410
|
|
8
|
+
prezo/export/pdf.py,sha256=9rzybk8o1vEu_JwvKB2abzrYoKfpEA8IP5pEFJHF5WM,13942
|
|
9
|
+
prezo/export/svg.py,sha256=gxhRXpLiKcVr4NSF8nwaid74p8f9dvj4RwXwvMP0EbU,5698
|
|
5
10
|
prezo/images/__init__.py,sha256=xrWSR3z0SXYpLtjIvR2VOMxiJGkxEsls5-EOs9GecFA,324
|
|
6
11
|
prezo/images/ascii.py,sha256=aNz02jN4rkDw0WzmkGDrAGw1R1dY5QGREvIIPI6jwow,7613
|
|
7
12
|
prezo/images/base.py,sha256=STuS57AVSJ2lzwyn0QrIceGaSd2IWEiLGN-elT3u3AM,2749
|
|
@@ -11,7 +16,7 @@ prezo/images/kitty.py,sha256=mWR-tIE_WDP5BjOkQydPpxWBBGNaZL8PkMICesWQid8,10883
|
|
|
11
16
|
prezo/images/overlay.py,sha256=lWIAvINxZrKembtB0gzWWaUoedNt7beFU4OhErfwWaw,9600
|
|
12
17
|
prezo/images/processor.py,sha256=zMcfwltecup_AX2FhUIlPdO7c87a9jw7P9tLTIkr54U,4069
|
|
13
18
|
prezo/images/sixel.py,sha256=2IeKDiMsWU1Tn3HYI3PC972ygxKGqpfz6tnhQcM_sVM,5604
|
|
14
|
-
prezo/layout.py,sha256=
|
|
19
|
+
prezo/layout.py,sha256=xy-UaZvU2ZDe0H7XjMCDb30tog4LRdZKB5OUh_r0aqo,19929
|
|
15
20
|
prezo/parser.py,sha256=f1eJtr9xVnxa35jgQ3GvKwNa6TvaXrIKQWgBB_geVAI,15058
|
|
16
21
|
prezo/screens/__init__.py,sha256=xHG9jNJz4vi1tpneSEVlD0D9I0M2U4gAGk6-R9xbUf4,508
|
|
17
22
|
prezo/screens/base.py,sha256=2n6Uj8evfIbcpn4AVYNG5iM_k7rIJ3Vwmor_xrQPU9E,2057
|
|
@@ -28,7 +33,7 @@ prezo/widgets/image_display.py,sha256=8IKncaoC2iWebmJQp_QomF7UVgRxD4WThOshN1Nht2
|
|
|
28
33
|
prezo/widgets/slide_button.py,sha256=g5mvtCZSorTIZp_PXgHYeYeeCSNFy0pW3K7iDlZu7yA,2012
|
|
29
34
|
prezo/widgets/slide_content.py,sha256=AO3doIuPBSo5vec_d1xE5F8YMJWxzOq5IFO7gSYSWNw,2300
|
|
30
35
|
prezo/widgets/status_bar.py,sha256=Wcun71kg2Q4s5aduPwTvS4kDHZj5p-zDmD7Cx3_ZFP4,8136
|
|
31
|
-
prezo-2026.1.
|
|
32
|
-
prezo-2026.1.
|
|
33
|
-
prezo-2026.1.
|
|
34
|
-
prezo-2026.1.
|
|
36
|
+
prezo-2026.1.3.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
|
|
37
|
+
prezo-2026.1.3.dist-info/entry_points.txt,sha256=74ShZJ_EKjzi63JyPynVnc0uCHGNjIWjAVs8vU_qTyA,38
|
|
38
|
+
prezo-2026.1.3.dist-info/METADATA,sha256=mv3i3_l0Rq03cmGDQzwBeZ9FiUIJ6enFmJNSvm83v2I,5320
|
|
39
|
+
prezo-2026.1.3.dist-info/RECORD,,
|