README.md

# Quillon

A pure Elixir library for structured document representation with rich text support, similar to ProseMirror/Tiptap/Slate in the JavaScript ecosystem.

## Core Concepts

### AST Structure

Documents are trees of nodes represented as tuples:

```elixir
{type, attrs, children}

# Example
{:paragraph, %{}, [{:text, %{text: "Hello", marks: [:bold]}, []}]}
```

### Element Types

| Type | Examples | Description |
|------|----------|-------------|
| **Block** | `paragraph`, `heading`, `image`, `table` | Vertical stacking elements |
| **Layout** | `row`, `grid` | Flex/grid container nodes |
| **Inline** | `text` | Text nodes with marks, flow within blocks |

### Marks System

Marks apply formatting to text nodes. Not markdown - structured data:

```elixir
# Simple marks (atoms)
:bold, :italic, :underline, :strike, :code, :subscript, :superscript

# Marks with attributes (tuples)
{:link, %{href: "https://example.com"}}
{:highlight, %{color: "yellow"}}
{:mention, %{id: "user_123", type: :user}}
```

### Layout & Styling

All block nodes accept optional Tailwind-inspired layout and styling tokens:

```elixir
# Layout: align, width, spacing, indent, valign
Quillon.paragraph("Centered text", align: :center, spacing: :lg)
Quillon.image("/photo.jpg", "Photo", width: :wide, rounded: :lg, shadow: :md)

# Container layouts
Quillon.row([card1, card2, card3], justify: :between, gap: :md)
Quillon.grid([item1, item2, item3, item4], columns: 2, gap: :sm)

# Styling: font_size, font_weight, color, background, border, rounded, shadow, opacity
Quillon.heading(1, "Alert", color: :danger, font_weight: :bold)
```

All properties use constrained value sets (atoms), not arbitrary CSS. Renderers map tokens to their own design system.

### Mark Configuration

| Option | Purpose |
|--------|---------|
| `inclusive` | New text at mark boundary inherits mark |
| `keep_on_split` | Mark persists when Enter splits node |
| `excludes` | Mutually exclusive marks (e.g., code excludes bold) |

## Key Algorithms

1. **Text Splitting** - Split at END offset first, then START (preserves positions)
2. **Normalization** - Merge adjacent text nodes with identical marks
3. **Loose Equality** - Compare marks only, ignore text content when merging
4. **Schema Validation** - Content expressions like `"block+"`, `"inline*"`

## Architecture Decisions

| Decision | Rationale |
|----------|-----------|
| No Grove dependency | Sync is separate concern; users may not need CRDT |
| No LiveView dependency | Keep core pure Elixir; framework-agnostic |
| Extensible schema | Support future extensions (markdown, math, custom blocks) |

## Installation

```elixir
def deps do
  [
    {:quillon, "~> 0.2.0"}
  ]
end
```

## Package Structure

```
quillon/           # Core - pure Elixir (this library)
quillon_live/      # LiveView components/hooks
quillon_grove/     # Grove CRDT integration
```

## Documentation

- [Document Model](guides/document_model.md) - Full architecture spec
- [Roadmap](guides/roadmap.md) - Development phases

## License

MIT