Skip to content

Commit b175613

Browse files
authored
Merge pull request #24 from posit-dev/blastula-style-authoring
feat: composing library
2 parents b9a53e6 + 3e2b0c1 commit b175613

File tree

19 files changed

+1695
-199
lines changed

19 files changed

+1695
-199
lines changed

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ preview:
55
cd docs && quarto preview
66

77
test:
8-
pytest nbmail/tests nbmail/mjml/tests --cov-report=xml
8+
pytest nbmail/tests nbmail/mjml/tests nbmail/compose/tests --cov-report=xml
99

1010
test-update:
11-
pytest nbmail/tests nbmail/mjml/tests --snapshot-update
11+
pytest nbmail/tests nbmail/mjml/tests nbmail/compose/tests --snapshot-update
1212

1313
generate-mjml-tags:
1414
python3 nbmail/mjml/scripts/generate_tags.py

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,13 @@ from nbmail import (
3838
)
3939

4040
# Read a Quarto email JSON file
41-
email_struct = quarto_json_to_email("email.json")
41+
email = quarto_json_to_email("email.json")
4242

4343
# Preview the email as HTML
44-
email_struct.write_preview_email("preview.html")
44+
email.write_preview_email("preview.html")
4545

4646
# Send the email via Gmail
47-
send_email_with_gmail("your_email@gmail.com", "your_password", email_struct)
47+
send_email_with_gmail("your_email@gmail.com", "your_password", email)
4848
```
4949

5050
## Features

docs/_quarto.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,22 @@ quartodoc:
7777
contents:
7878
- write_email_message_to_file
7979

80+
- title: Templated Authoring
81+
desc: >
82+
Write responsive emails with the Blastula template
83+
package: nbmail
84+
contents:
85+
- compose.compose_email
86+
- compose.create_blocks
87+
- compose.block_text
88+
- compose.block_title
89+
- compose.block_spacer
90+
- compose.block_image
91+
- compose.block_plot
92+
- compose.md
93+
- compose.add_cta_button
94+
- compose.add_readable_time
95+
8096
- title: MJML Authoring
8197
desc: >
8298
Write responsive emails with MJML

nbmail/compose/README.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# nbmail.compose
2+
3+
The `compose` module provides a high-level, Pythonic API for building HTML emails using simple building blocks. It's designed for data science workflows where you need to create professional-looking emails without writing raw HTML or MJML.
4+
5+
## Quick Start
6+
7+
```python
8+
from nbmail.compose import compose_email, block_text, block_title, create_blocks
9+
10+
# Simple email
11+
email = compose_email(
12+
body=block_text("Hello world!")
13+
)
14+
15+
# Email with multiple blocks
16+
email = compose_email(
17+
title="Weekly Report",
18+
body=create_blocks(
19+
block_text("Welcome to this week's update!"),
20+
block_text("Here's what's new...")
21+
)
22+
)
23+
```
24+
25+
## Architecture
26+
27+
The compose module is built on three main concepts:
28+
29+
1. **Blocks** - Individual content units (text, images, plots, spacers, markdown)
30+
2. **BlockList** - Collections of blocks that can be rendered together
31+
3. **compose_email()** - The main entry point that converts blocks to MJML and then to Email objects
32+
33+
### Why Blocks?
34+
35+
Blocks provide a simple, composable abstraction over MJML's more complex tag structure. They encapsulate common email patterns (text sections, images, spacers) in an easy-to-use API.
36+
37+
### MJML Under the Hood
38+
39+
Blocks compile to MJML tags internally. MJML provides:
40+
- Responsive design by default
41+
- Cross-client compatibility
42+
- Semantic structure (sections, columns, etc.)
43+
44+
### Inline Attachments
45+
46+
Local images are read as bytes and stored in `Email.inline_attachments` as base64 strings. During sending, these are converted to CID references in the MIME structure.
47+
48+
### Jupyter Display Support
49+
50+
Blocks and BlockLists implement `_repr_html_()` for rich display in notebooks:
51+
52+
```python
53+
# In a Jupyter notebook:
54+
block = block_text("Preview me!")
55+
block # Automatically renders as HTML
56+
```
57+
58+
## Dependencies
59+
60+
**Required:**
61+
- `markdown` - For Markdown processing in text blocks
62+
63+
**Optional:**
64+
- `plotnine` - For `block_plot()` functionality
65+
66+
67+
68+
## API Reference
69+
70+
Complete API documentation is available in the [nbmail reference docs](https://posit-dev.github.io/nbmail/reference/).

nbmail/compose/__init__.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from .compose import compose_email, create_blocks
2+
from .blocks import block_text, block_title, block_spacer, block_image, block_plot
3+
from .inline_utils import (
4+
md,
5+
add_cta_button,
6+
add_readable_time,
7+
)
8+
9+
__all__ = (
10+
"compose_email",
11+
"create_blocks",
12+
"block_text",
13+
"block_title",
14+
"block_spacer",
15+
"block_image",
16+
"block_plot",
17+
"md",
18+
"add_cta_button",
19+
"add_readable_time",
20+
)

0 commit comments

Comments
 (0)