Skip to content

Commit c66df63

Browse files
feat: Add HTML message formatting and tests
This commit introduces HTML formatting for messages, including support for various blocks like headers, text, lists, tables, and expandable sections. It also adds comprehensive unit tests with corresponding HTML fixtures to ensure correct rendering. Co-authored-by: mika <mika@elementary-data.com>
1 parent 4df22dc commit c66df63

38 files changed

Lines changed: 67 additions & 14 deletions

elementary/messages/formats/html.py

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from __future__ import annotations
22

33
from html import escape
4-
from typing import Iterable, Sequence
4+
from typing import Sequence
55

66
from elementary.messages.blocks import (
77
ActionBlock,
@@ -56,15 +56,13 @@ def format(self, message: MessageBody) -> str:
5656
container_style = self._build_container_style(message.color)
5757
return f'<div style="{container_style}">{body_html}</div>'
5858

59-
def format_message_blocks(
60-
self, blocks: Sequence[MessageBlock | ExpandableBlock]
61-
) -> str:
62-
formatted_blocks = [
63-
self.format_message_block(block)
64-
for block in blocks
65-
if (formatted := self.format_message_block(block))
66-
]
67-
return "".join(formatted_blocks)
59+
def format_message_blocks(self, blocks: Sequence[MessageBlock]) -> str:
60+
rendered: list[str] = []
61+
for block in blocks:
62+
formatted = self.format_message_block(block)
63+
if formatted:
64+
rendered.append(formatted)
65+
return "".join(rendered)
6866

6967
def format_message_block(self, block: MessageBlock | ExpandableBlock) -> str:
7068
if isinstance(block, HeaderBlock):
@@ -129,7 +127,7 @@ def _format_inline_block(self, block: InlineBlock) -> str:
129127
f"{escape(block.code)}</code>"
130128
)
131129
elif isinstance(block, MentionBlock):
132-
return f'<span style="color:#0ea5e9;">@{escape(block.user)}</span>'
130+
return f'<span style="color:#0ea5e9;">{escape(block.user)}</span>'
133131
elif isinstance(block, LineBlock):
134132
return self._format_line_block(block)
135133
elif isinstance(block, WhitespaceBlock):
@@ -143,13 +141,13 @@ def _format_line_block(self, block: LineBlock) -> str:
143141
return separator.join(inlines)
144142

145143
def _format_lines_block(self, block: LinesBlock) -> str:
146-
lines = [
144+
lines_html = [
147145
f'<div style="margin:0;">{self._format_line_block(line_block)}</div>'
148146
for line_block in block.lines
149147
]
150-
if not lines:
148+
if not lines_html:
151149
return ""
152-
return f'<div style="{self._SECTION_MARGIN}{";".join([])}">' + "".join(lines) + "</div>"
150+
return self._wrap_section("".join(lines_html))
153151

154152
def _format_fact_list_block(self, block: FactListBlock) -> str:
155153
if not block.facts:
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<div style="font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;font-size:14px;line-height:1.5;color:#1f2937;background-color:#ffffff;border:1px solid #e5e7eb;border-radius:6px;padding:16px;border-left:4px solid #33b989;padding-left:12px"><div style="margin:0 0 12px"><h1 style="margin:0;font-size:18px;line-height:1.4;">Main Header</h1></div><div style="margin:0 0 12px"><div style="margin:0;">Normal text <strong>Bold text</strong> <em>Italic text</em></div></div><div style="margin:0 0 12px"><div style="margin:0;">- First bullet point</div><div style="margin:0;">- Second bullet point</div></div><div style="margin:0 0 12px"><div style="margin:0;"><span style="margin-right:4px;"></span> Check item</div></div><div style="margin:0 0 12px"><table style="width:100%;border-collapse:separate;border-spacing:0 6px;"><tr><td style="padding:4px 12px;font-weight:600;color:#111827;background-color:#f3f4f6;border-radius:4px 0 0 4px;white-space:nowrap;">Status</td><td style="padding:4px 12px;border:1px solid #f3f4f6;border-left:none;border-radius:0 4px 4px 0;font-weight:400;">Passed</td></tr><tr><td style="padding:4px 12px;font-weight:600;color:#111827;background-color:#f3f4f6;border-radius:4px 0 0 4px;white-space:nowrap;">Tags</td><td style="padding:4px 12px;border:1px solid #f3f4f6;border-left:none;border-radius:0 4px 4px 0;font-weight:400;">test, example</td></tr></table></div><div style="border:1px solid #e5e7eb;border-radius:6px;margin:16px 0;overflow:hidden;"><div style="margin:0;padding:12px 16px;font-weight:600;background-color:#f8fafc;">Show Details</div><div style="padding:12px 16px;"><div style="margin:0 0 12px"><div style="margin:0;"><span style="margin-right:4px;">🔎</span> <strong>Details Section</strong></div><div style="margin:0;">Here&#x27;s some content with a <a style="color:#2563eb;text-decoration:none;" href="https://example.com" target="_blank" rel="noopener noreferrer">link</a></div></div></div></div><div style="margin:0 0 12px"><div style="margin:0;"><span style="color:#0ea5e9;">user1</span> <code style="font-family:'SFMono-Regular',Consolas,'Liberation Mono',Menlo,monospace;background-color:#eef2ff;border-radius:3px;padding:1px 4px;font-size:12px;">select 1</code></div></div></div>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<div style="font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;font-size:14px;line-height:1.5;color:#1f2937;background-color:#ffffff;border:1px solid #e5e7eb;border-radius:6px;padding:16px"><div style="margin:0 0 12px"><h1 style="margin:0;font-size:18px;line-height:1.4;">Main Header</h1></div><div style="margin:0 0 12px"><div style="margin:0;">Normal text <strong>Bold text</strong> <em>Italic text</em></div></div><div style="margin:0 0 12px"><div style="margin:0;">- First bullet point</div><div style="margin:0;">- Second bullet point</div></div><div style="margin:0 0 12px"><div style="margin:0;"><span style="margin-right:4px;"></span> Check item</div></div><div style="margin:0 0 12px"><table style="width:100%;border-collapse:separate;border-spacing:0 6px;"><tr><td style="padding:4px 12px;font-weight:600;color:#111827;background-color:#f3f4f6;border-radius:4px 0 0 4px;white-space:nowrap;">Status</td><td style="padding:4px 12px;border:1px solid #f3f4f6;border-left:none;border-radius:0 4px 4px 0;font-weight:400;">Passed</td></tr><tr><td style="padding:4px 12px;font-weight:600;color:#111827;background-color:#f3f4f6;border-radius:4px 0 0 4px;white-space:nowrap;">Tags</td><td style="padding:4px 12px;border:1px solid #f3f4f6;border-left:none;border-radius:0 4px 4px 0;font-weight:400;">test, example</td></tr></table></div><div style="border:1px solid #e5e7eb;border-radius:6px;margin:16px 0;overflow:hidden;"><div style="margin:0;padding:12px 16px;font-weight:600;background-color:#f8fafc;">Show Details</div><div style="padding:12px 16px;"><div style="margin:0 0 12px"><div style="margin:0;"><span style="margin-right:4px;">🔎</span> <strong>Details Section</strong></div><div style="margin:0;">Here&#x27;s some content with a <a style="color:#2563eb;text-decoration:none;" href="https://example.com" target="_blank" rel="noopener noreferrer">link</a></div></div></div></div><div style="margin:0 0 12px"><div style="margin:0;"><span style="color:#0ea5e9;">user1</span> <code style="font-family:'SFMono-Regular',Consolas,'Liberation Mono',Menlo,monospace;background-color:#eef2ff;border-radius:3px;padding:1px 4px;font-size:12px;">select 1</code></div></div></div>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<div style="font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;font-size:14px;line-height:1.5;color:#1f2937;background-color:#ffffff;border:1px solid #e5e7eb;border-radius:6px;padding:16px;border-left:4px solid #ff0000;padding-left:12px"><div style="margin:0 0 12px"><h1 style="margin:0;font-size:18px;line-height:1.4;">Main Header</h1></div><div style="margin:0 0 12px"><div style="margin:0;">Normal text <strong>Bold text</strong> <em>Italic text</em></div></div><div style="margin:0 0 12px"><div style="margin:0;">- First bullet point</div><div style="margin:0;">- Second bullet point</div></div><div style="margin:0 0 12px"><div style="margin:0;"><span style="margin-right:4px;"></span> Check item</div></div><div style="margin:0 0 12px"><table style="width:100%;border-collapse:separate;border-spacing:0 6px;"><tr><td style="padding:4px 12px;font-weight:600;color:#111827;background-color:#f3f4f6;border-radius:4px 0 0 4px;white-space:nowrap;">Status</td><td style="padding:4px 12px;border:1px solid #f3f4f6;border-left:none;border-radius:0 4px 4px 0;font-weight:400;">Passed</td></tr><tr><td style="padding:4px 12px;font-weight:600;color:#111827;background-color:#f3f4f6;border-radius:4px 0 0 4px;white-space:nowrap;">Tags</td><td style="padding:4px 12px;border:1px solid #f3f4f6;border-left:none;border-radius:0 4px 4px 0;font-weight:400;">test, example</td></tr></table></div><div style="border:1px solid #e5e7eb;border-radius:6px;margin:16px 0;overflow:hidden;"><div style="margin:0;padding:12px 16px;font-weight:600;background-color:#f8fafc;">Show Details</div><div style="padding:12px 16px;"><div style="margin:0 0 12px"><div style="margin:0;"><span style="margin-right:4px;">🔎</span> <strong>Details Section</strong></div><div style="margin:0;">Here&#x27;s some content with a <a style="color:#2563eb;text-decoration:none;" href="https://example.com" target="_blank" rel="noopener noreferrer">link</a></div></div></div></div><div style="margin:0 0 12px"><div style="margin:0;"><span style="color:#0ea5e9;">user1</span> <code style="font-family:'SFMono-Regular',Consolas,'Liberation Mono',Menlo,monospace;background-color:#eef2ff;border-radius:3px;padding:1px 4px;font-size:12px;">select 1</code></div></div></div>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<div style="font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;font-size:14px;line-height:1.5;color:#1f2937;background-color:#ffffff;border:1px solid #e5e7eb;border-radius:6px;padding:16px;border-left:4px solid #ffcc00;padding-left:12px"><div style="margin:0 0 12px"><h1 style="margin:0;font-size:18px;line-height:1.4;">Main Header</h1></div><div style="margin:0 0 12px"><div style="margin:0;">Normal text <strong>Bold text</strong> <em>Italic text</em></div></div><div style="margin:0 0 12px"><div style="margin:0;">- First bullet point</div><div style="margin:0;">- Second bullet point</div></div><div style="margin:0 0 12px"><div style="margin:0;"><span style="margin-right:4px;"></span> Check item</div></div><div style="margin:0 0 12px"><table style="width:100%;border-collapse:separate;border-spacing:0 6px;"><tr><td style="padding:4px 12px;font-weight:600;color:#111827;background-color:#f3f4f6;border-radius:4px 0 0 4px;white-space:nowrap;">Status</td><td style="padding:4px 12px;border:1px solid #f3f4f6;border-left:none;border-radius:0 4px 4px 0;font-weight:400;">Passed</td></tr><tr><td style="padding:4px 12px;font-weight:600;color:#111827;background-color:#f3f4f6;border-radius:4px 0 0 4px;white-space:nowrap;">Tags</td><td style="padding:4px 12px;border:1px solid #f3f4f6;border-left:none;border-radius:0 4px 4px 0;font-weight:400;">test, example</td></tr></table></div><div style="border:1px solid #e5e7eb;border-radius:6px;margin:16px 0;overflow:hidden;"><div style="margin:0;padding:12px 16px;font-weight:600;background-color:#f8fafc;">Show Details</div><div style="padding:12px 16px;"><div style="margin:0 0 12px"><div style="margin:0;"><span style="margin-right:4px;">🔎</span> <strong>Details Section</strong></div><div style="margin:0;">Here&#x27;s some content with a <a style="color:#2563eb;text-decoration:none;" href="https://example.com" target="_blank" rel="noopener noreferrer">link</a></div></div></div></div><div style="margin:0 0 12px"><div style="margin:0;"><span style="color:#0ea5e9;">user1</span> <code style="font-family:'SFMono-Regular',Consolas,'Liberation Mono',Menlo,monospace;background-color:#eef2ff;border-radius:3px;padding:1px 4px;font-size:12px;">select 1</code></div></div></div>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<div style="font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;font-size:14px;line-height:1.5;color:#1f2937;background-color:#ffffff;border:1px solid #e5e7eb;border-radius:6px;padding:16px"><div style="margin:0 0 12px"><div style="margin:0;">RED_TRIANGLE <span style="margin-right:4px;">🔺</span> X <span style="margin-right:4px;"></span> WARNING <span style="margin-right:4px;">⚠️</span> EXCLAMATION <span style="margin-right:4px;"></span> CHECK <span style="margin-right:4px;"></span> MAGNIFYING_GLASS <span style="margin-right:4px;">🔎</span> HAMMER_AND_WRENCH <span style="margin-right:4px;">🛠️</span> POLICE_LIGHT <span style="margin-right:4px;">🚨</span> INFO <span style="margin-right:4px;">ℹ️</span> EYE <span style="margin-right:4px;">👁️</span> GEAR <span style="margin-right:4px;">⚙️</span> BELL <span style="margin-right:4px;">🔔</span> GEM <span style="margin-right:4px;">💎</span> SPARKLES <span style="margin-right:4px;"></span></div></div></div>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<div style="font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;font-size:14px;line-height:1.5;color:#1f2937;background-color:#ffffff;border:1px solid #e5e7eb;border-radius:6px;padding:16px"><div style="margin:0 0 12px"><div style="margin:0;">- First bullet</div><div style="margin:0;">- Second bullet</div></div><div style="margin:0 0 12px"><div style="margin:0;"><span style="margin-right:4px;"></span> Check item 1</div><div style="margin:0;"><span style="margin-right:4px;"></span> Check item 2</div></div></div>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<div style="font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;font-size:14px;line-height:1.5;color:#1f2937;background-color:#ffffff;border:1px solid #e5e7eb;border-radius:6px;padding:16px"><div style="margin:0 0 12px"><pre style="margin:0;padding:12px;background-color:#f8fafc;border-radius:4px;font-family:'SFMono-Regular',Consolas,'Liberation Mono',Menlo,monospace;font-size:13px;line-height:1.5;white-space:pre-wrap;">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, c</pre></div></div>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<div style="font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;font-size:14px;line-height:1.5;color:#1f2937;background-color:#ffffff;border:1px solid #e5e7eb;border-radius:6px;padding:16px"><div style="margin:0 0 12px"><pre style="margin:0;padding:12px;background-color:#f8fafc;border-radius:4px;font-family:'SFMono-Regular',Consolas,'Liberation Mono',Menlo,monospace;font-size:13px;line-height:1.5;white-space:pre-wrap;">Lorem ipsum dolor sit amet, consectetur adipiscing</pre></div></div>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<div style="font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;font-size:14px;line-height:1.5;color:#1f2937;background-color:#ffffff;border:1px solid #e5e7eb;border-radius:6px;padding:16px"><div style="margin:0 0 12px"><pre style="margin:0;padding:12px;background-color:#f8fafc;border-radius:4px;font-family:'SFMono-Regular',Consolas,'Liberation Mono',Menlo,monospace;font-size:13px;line-height:1.5;white-space:pre-wrap;">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adip</pre></div></div>

0 commit comments

Comments
 (0)