dog-markdown 1.0.2
Add your description here
Requires Python: >=3.13

# dog-markdown

A type-safe Markdown generator for Python, built with Pydantic.

## Features

- **Type-safe**: Uses Pydantic models to ensure valid Markdown structure
- **Extensible**: Easy to add new Markdown elements
- **Human-friendly**: Automatic spacing and formatting
- **Nested elements**: Supports deeply nested structures like nested blockquotes

## Installation

```bash
uv add dog-markdown
```

## API Documentation

### Core Model API

Create structured Markdown documents using Pydantic models directly.

#### Document

Top-level document container that holds multiple Markdown elements.

```python
from dog_markdown import Document, Heading, Paragraph, Text

doc = Document(
    children=[
        Heading(level=1, content="My Document"),
        Paragraph(children=[Text(content="Hello World!")])
    ]
)

print(doc.to_str())
```

**Parameters**:
- `children`: List of Markdown elements contained in the document. Supports `Heading`, `Paragraph`, `UnorderedList`, `CodeBlock`, `Blockquote`, `Table`, `Document`

**Methods**:
- `to_str()`: Convert document to Markdown string

#### Heading

Heading element supporting levels H1-H9.

```python
from dog_markdown import Heading

# Basic usage
heading = Heading(level=2, content="Section Title")

# Using Paragraph as content (supports rich text)
from dog_markdown import Paragraph, Text, Link
heading = Heading(
    level=3,
    content=Paragraph(children=[
        Text(content="See our "),
        Link(text="API docs", url="/api"),
        Text(content=" for details")
    ])
)
```

**Parameters**:
- `level`: Heading level (1-9)
- `content`: Heading content, supports string or `Paragraph` object

#### Paragraph

Paragraph element that can contain text, links, and images.

```python
from dog_markdown import Paragraph, Text, Link, Image

# Basic text paragraph
paragraph = Paragraph(children=[Text(content="Simple text paragraph")])

# Rich text paragraph
paragraph = Paragraph(children=[
    Text(content="Visit our "),
    Link(text="website", url="https://example.com"),
    Text(content=" or check out our "),
    Image(alt="Logo", url="/logo.png")
])
```

**Parameters**:
- `children`: List of paragraph content. Supports `Text`, `Link`, `Image`

#### Text

Plain text element with optional bold formatting.

```python
from dog_markdown import Text

# Plain text
text = Text(content="Plain text")

# Bold text
bold_text = Text(content="Important note", bold=True)
```

**Parameters**:
- `content`: Text content
- `bold`: Whether to render text as bold (default: False)

#### Link

Hyperlink element.

```python
from dog_markdown import Link

# Basic link
link = Link(text="Click me", url="https://example.com")

# Link with title
link = Link(text="Download", url="/file.pdf", title="Download PDF document")
```

**Parameters**:
- `text`: Link display text
- `url`: Target URL
- `title`: Optional tooltip title displayed on hover

#### Image

Image element with support for alternative text and titles.

```python
from dog_markdown import Image

# Basic image
image = Image(alt="Cat picture", url="/cat.jpg")

# Image with title
image = Image(alt="Dog picture", url="/dog.jpg", title="A cute dog")
```

**Parameters**:
- `alt`: Alternative text for accessibility
- `url`: Image URL or path
- `title`: Optional tooltip title displayed on hover

#### UnorderedList

Unordered (bullet) list.

```python
from dog_markdown import UnorderedList, ListItem, Document, Paragraph, Text

# Simple list
list = UnorderedList(
    items=[
        ListItem(content=Document(children=[Paragraph(children=[Text(content="Item 1")])])),
        ListItem(content=Document(children=[Paragraph(children=[Text(content="Item 2")])]))
    ]
)

# Nested list
nested_list = UnorderedList(
    items=[
        ListItem(content=Document(children=[
            Paragraph(children=[Text(content="Parent item")]),
            UnorderedList(
                items=[
                    ListItem(content=Document(children=[Paragraph(children=[Text(content="Child item 1")])])),
                    ListItem(content=Document(children=[Paragraph(children=[Text(content="Child item 2")])]))
                ]
            )
        ]))
    ]
)
```

**Parameters**:
- `items`: List of `ListItem` objects
- `indent`: Indentation level (default: 1)

#### ListItem

List item element.

```python
from dog_markdown import ListItem, Document, Paragraph, Text

list_item = ListItem(
    content=Document(children=[Paragraph(children=[Text(content="List item content")])])
)
```

**Parameters**:
- `content`: List item content, a `Document` object

#### CodeBlock

Code block element with optional syntax highlighting.

```python
from dog_markdown import CodeBlock

# Basic code block
code_block = CodeBlock(content="print('Hello World')")

# Code block with syntax highlighting
python_code = CodeBlock(
    content="def add(a, b):\n    return a + b",
    language="python"
)
```

**Parameters**:
- `content`: Code content
- `language`: Optional programming language name for syntax highlighting

#### Blockquote

Blockquote element that supports nesting.

```python
from dog_markdown import Blockquote, Document, Paragraph, Text

# Basic quote
quote = Blockquote(
    content=Document(children=[Paragraph(children=[Text(content="To be or not to be")])])
)

# Nested quote
nested_quote = Blockquote(
    content=Document(children=[
        Paragraph(children=[Text(content="Outer quote")]),
        Blockquote(
            content=Document(children=[Paragraph(children=[Text(content="Inner quote")])])
        )
    ])
)
```

**Parameters**:
- `content`: Quote content, a `Document` object

#### Table

Table element with support for column alignment.

```python
from dog_markdown import Table

# Basic table
table = Table(
    headers=["Name", "Age", "Email"],
    rows=[
        ["Alice", "30", "alice@example.com"],
        ["Bob", "25", "bob@example.com"]
    ]
)

# Table with alignment
table = Table(
    headers=["Name", "Age", "Email"],
    rows=[
        ["Alice", "30", "alice@example.com"],
        ["Bob", "25", "bob@example.com"]
    ],
    align=["left", "center", "right"]
)

# Rich text table
table = Table(
    headers=["Product", "Link", "Image"],
    rows=[
        [
            "Laptop",
            Link(text="Buy now", url="/laptop"),
            Image(alt="Laptop", url="/laptop.jpg")
        ]
    ]
)
```

**Parameters**:
- `headers`: List of table column headers
- `rows`: List of table data rows. Each cell supports strings, `Text`, `Link`, `Image`
- `align`: Optional list of column alignments. Supports `left`, `center`, `right`. Defaults to left alignment.

### Builder API

Fluent API for building Markdown documents procedurally, ideal for programmatic creation.

#### Basic Usage

```python
from dog_markdown import MarkdownBuilder

builder = MarkdownBuilder()
doc = builder
    .add_heading(1, "My Document")
    .add_paragraph("Welcome to dog-markdown!")
    .add_code_block("print('Hello World')", language="python")
    .build()

print(doc.to_str())
```

#### Quick Start

Use the `markdown()` function to quickly create a Builder:

```python
from dog_markdown import markdown

doc = markdown()
    .add_heading(2, "Quick Start")
    .add_paragraph("Easy to use builder API")
    .to_str()
```

#### Rich Text Paragraphs

```python
# Basic usage
builder.add_paragraph([
    "Visit our ",
    builder.link("website", "https://example.com"),
    " for more info"
])

# Create complex paragraphs using context managers
with builder.paragraph():
    builder.add_bold_text("Note:")
    builder.add_text(" This is a ")
    builder.add_bold_text("very important")
    builder.add_text(" message with ")
    builder.add_link("links", "https://example.com")
```

#### List Operations

Use `add_paragraph` directly within lists to simplify API usage:

```python
# Create lists using context managers
with builder.unordered_list():
    # Simple list item
    builder.add_paragraph("Simple list item")
    
    # Rich text list item
    with builder.paragraph():
        builder.add_text("Item with a ")
        builder.add_link("link", "https://example.com")
        builder.add_text(" and ")
        builder.add_bold_text("bold text")
    
    # Complex list item with multiple paragraphs
    with builder.paragraph():
        builder.add_bold_text("Multi-paragraph item:")
    builder.add_paragraph("This is the first paragraph")
    builder.add_paragraph("This is the second paragraph")
    
    # Nested lists
    with builder.unordered_list():
        builder.add_paragraph("Nested item 1")
        builder.add_paragraph("Nested item 2")
```

#### Complex Document Example

```python
from dog_markdown import MarkdownBuilder

builder = MarkdownBuilder()

# Create document title and description
builder.add_heading(1, "My Project")
builder.add_paragraph("A type-safe Markdown generator for Python")

# Add features list
builder.add_heading(2, "Features")
with builder.unordered_list():
    with builder.paragraph():
        builder.add_bold_text("Type-safe")
        builder.add_text(": Uses Pydantic models to ensure valid Markdown structure")
    
    with builder.paragraph():
        builder.add_bold_text("Extensible")
        builder.add_text(": Easy to add new Markdown elements")
    
    builder.add_paragraph("Human-friendly automatic spacing and formatting")

# Add code example
builder.add_heading(2, "Example")
builder.add_code_block(
    "def hello():\n    print('Hello World!')",
    language="python"
)

# Add blockquote
builder.add_blockquote([
    "This is a blockquote",
    builder.paragraph("It supports multiple paragraphs and "),
    builder.paragraph(["even ", builder.bold_text("bold text"), " or ", builder.link("links", "https://example.com")])
])

# Add table
builder.add_table(
    headers=["Feature", "Supported", "Description"],
    rows=[
        ["Type safety", "✓", "Uses Pydantic models"],
        ["Nested elements", "✓", "Deeply nested structures"],
        ["Tables", "✓", "With alignment support"]
    ],
    align=["left", "center", "left"]
)

# Output result
print(builder.to_str())
```

#### Builder Method Reference

##### Document Structure

- `add_heading(level: int, content: str | Paragraph)`: Add a heading
- `add_paragraph(content: str | List[str | Text | Link | Image] | Paragraph)`: Add a paragraph
- `add_code_block(content: str, language: str | None = None)`: Add a code block
- `add_blockquote(content: str | Document | List[...])`: Add a blockquote
- `add_table(headers: List[str], rows: List[List[...]], align: List[str | None] | None = None)`: Add a table
- `build()`: Build and return the final `Document`
- `to_str()`: Build and return the document as a Markdown string

##### List Operations

- `start_unordered_list()`: Start an unordered list
- `end_unordered_list()`: End the current unordered list
- `unordered_list()`: Return a list context manager

##### Context Managers

- `paragraph()`: Context manager for creating complex rich text paragraphs
- `unordered_list()`: Context manager for creating nested lists

##### Text Elements

- `add_link(text: str, url: str, title: str | None = None)`: Create a link element (for nesting in paragraphs)
- `add_image(alt: str, url: str, title: str | None = None)`: Create an image element (for nesting in paragraphs)
- `add_text(content: str)`: Create a plain text element (for nesting in paragraphs)
- `add_bold_text(content: str)`: Create a bold text element (for nesting in paragraphs)

## Advanced Usage

### Nested Elements

```python
from dog_markdown import Document, Heading, Blockquote, Paragraph, Text, UnorderedList, ListItem

doc = Document(
    children=[
        Heading(level=1, content="Nested Example"),
        Blockquote(
            content=Document(
                children=[
                    Paragraph(children=[Text(content="Quote with list:")]),
                    UnorderedList(
                        items=[
                            ListItem(content=Document(children=[Paragraph(children=[Text(content="Item 1")])])),
                            ListItem(content=Document(children=[Paragraph(children=[Text(content="Item 2")])]))
                        ]
                    )
                ]
            )
        )
    ]
)
```

### Generating Complex Documents

Combine Builder API context managers to create very complex document structures:

```python
from dog_markdown import MarkdownBuilder

builder = MarkdownBuilder()

# Create technical documentation
builder.add_heading(1, "API Documentation")

builder.add_heading(2, "Introduction")
builder.add_paragraph("Welcome to our API documentation. This guide will help you get started.")

builder.add_heading(2, "Getting Started")
with builder.unordered_list():
    builder.add_paragraph("Sign up for an API key")
    builder.add_paragraph("Install the SDK")
    builder.add_paragraph("Make your first request")

builder.add_heading(3, "Authentication")
builder.add_paragraph("Authenticate your requests using API keys in the Authorization header.")
builder.add_code_block(
    "import requests\n\nheaders = {\n    'Authorization': 'Bearer YOUR_API_KEY'\n}\n\nresponse = requests.get('https://api.example.com/data', headers=headers)",
    language="python"
)

builder.add_heading(2, "Endpoints")

with builder.unordered_list():
    with builder.paragraph():
        builder.add_bold_text("GET /api/data")
        builder.add_text(": Retrieve data from the server")
    
    with builder.paragraph():
        builder.add_bold_text("POST /api/data")
        builder.add_text(": Create new data on the server")
    
    with builder.paragraph():
        builder.add_bold_text("PUT /api/data/{id}")
        builder.add_text(": Update existing data")

print(builder.to_str())
```

## Output Example

The following code:

```python
from dog_markdown import markdown

md = markdown()
    .add_heading(1, "My Project")
    .add_paragraph("A type-safe Markdown generator for Python")
    .add_blockquote("The best way to create structured Markdown")
    .start_unordered_list()
        .add_paragraph("Type-safe with Pydantic")
        .add_paragraph("Fluent builder API")
        .add_paragraph("Nested element support")
    .end_unordered_list()
    .add_table(
        headers=["Feature", "Status"],
        rows=[
            ["Type safety", "✅"],
            ["Builder API", "✅"],
            ["Nested elements", "✅"]
        ],
        align=["left", "center"]
    )
    .to_str()

print(md)
```

Will generate:

```markdown
# My Project

A type-safe Markdown generator for Python

> The best way to create structured Markdown

- Type-safe with Pydantic
- Fluent builder API
- Nested element support

| Feature | Status |
| --- | :---: |
| Type safety | ✅ |
| Builder API | ✅ |
| Nested elements | ✅ |
```

## Development

### Setup

```bash
uv sync
```

### Testing

```bash
uv run pytest
```

### Linting

```bash
uv run ruff check .
```

### Formatting

```bash
uv run ruff format .
```

## License

MIT