Metadata-Version: 2.4
Name: signalwire-asset-builder
Version: 1.0.30
Summary: SignalWire branded asset builder: slide decks and landing pages from DTCG design tokens
Author-email: SignalWire <open.source@signalwire.com>
License: MIT
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: python-pptx>=1.0
Requires-Dist: cairosvg>=2.7
Requires-Dist: lxml>=4.9
Provides-Extra: landing
Requires-Dist: pygments>=2.15; extra == "landing"
Provides-Extra: all
Requires-Dist: pygments>=2.15; extra == "all"

# SignalWire Asset Builder

Build branded SignalWire slide decks AND landing pages from Python. DTCG design tokens, locked brand colors, dark-mode-first. The slide builder ships 29 layouts with auto-fit text; the landing page builder ships 25 section types with a recipe-driven aesthetic system.

## Install

```bash
# Slides only
pip install signalwire-asset-builder

# Slides + landing pages
pip install signalwire-asset-builder[landing]
```

---

## Landing pages

Write a JSON content file with typed sections, then:

```bash
signalwire-assets landing build page.json -o page.html
```

The result is a self-contained HTML file with embedded CSS, fonts, logos, and optional tracking. No external deps at serve time.

### The guide has three parts (no universal recipe)

Different page purposes call for different compositions. The guide is:

1. **Principles** — judgment, not shape. Restraint is strategic; one thing dominates; data zones stay clean; atmospheric treatment is rhythmic, not uniform; controls serve content.
2. **Hard rules** — the validator enforces these because they cause real bugs or silent content drops. See "Forbidden combinations" below.
3. **Archetype samples** — illustrative patterns for common page purposes. Use as reference, not as templates. Deviate when the content calls for it.

The hero default is `dots-orbs`; the CTA default is `logo-slow`. Set `bg: "none"` on hero/cta to opt out of the default. All other section types default to no bg.

### Archetype samples

Each entry below is ONE way to compose a page of that type. Use as a reference, not a template.

- **Synthesis / alignment / thesis page:** hero nebula, clean middle, ONE `card: "blast"` on the thesis features, CTA matching `-up`. Restraint is the message.
- **Benchmark / data-heavy page:** hero nebula or default, data sections (benchmark/table/versus) stay clean, prose sections between data blocks CAN take atmospheric bgs to break reading fatigue, callouts work well for methodology notes (more than one is fine on dense pages), one `card: "blast"` on the key finding, CTA plain or matching-up.
- **Thought-leadership essay:** hero nebula or default, prose sections that mark pivots in the argument can take atmospheric bgs (others stay clean so the texture means something), closing section can be `sliced-ocean + card: "glow"` pair as the visual climax.
- **Product tour / feature walkthrough:** default hero, clean middle with `tilt: true` on features, one callout for the standout insight, default CTA.
- **Technical reference / documentation:** minimal atmosphere. Content is the aesthetic.
- **Announcement / launch:** more atmospheric. `sliced-drip` animated hero, `blast_grad` on showcase features, `logo-slow` CTA.

When the page does not fit any archetype cleanly, think about what IT is trying to DO and build from the principles.

### Controls

**Backgrounds — three distinct roles:**
- **Nebula family** (standalone atmospheric bg): `sliced-drip`, `sliced-hair`, `sliced-rain`, `sliced-edges`, `sliced-corner` (all with `-up` variants). Multi-color blurred clouds + scan lines.
- **Glow-layer family** (color layer BEHIND a card, never standalone): `sliced-glow`, `sliced-ocean`, `sliced-teal` (with `-up` variants). Flat single-color gradient. Requires `card: "glow"` or `card: "blast"` on the same section.
- **Base patterns** (subtle texture, at most ONE per page): `dots`, `grid`, `hex`, `diagonal`, `rings`, `orbs`, `noise`. Prefer `hex` over `dots` (dots gets overused).
- **Layered** (hero only): `dots-orbs`, `grid-orbs`.
- **Logo** (CTA only): `logo`, `logo-slow`, `logo-med`, `logo-fast`.

**Card wrappers** (`card` field, max ONE per page):
- `card: "blast"` — radial color glow behind panel. Accepts `blast_color: "R,G,B"` (default fuchsia).
- `card: "blast_grad"` — two-color gradient glow. Accepts `blast_color_1` and `blast_color_2`.
- `card: "glow"` — subtle shadow lift, no color glow. Pairs with glow-layer sliced bgs.

**Interactive effects:**
- Top-level `effects: {}` for page-level: `glow: true` (cursor follow), `reveal_bg: true` (scroll fade-in). Safe to enable globally.
- Per section: `tilt: true` (features/pricing cards), `magnetic: true` (CTA button only), `parallax: 0.3` (hero only), `typing: true` (one punchline code block).

### Forbidden combinations (the builder warns)

These are the rules the validator actually enforces. They prevent real bugs and silent content drops.

1. `bg` + `card` on the same section, EXCEPT `card: "glow"` + a glow-layer sliced bg (the intended pair).
2. `tilt` + `magnetic` on the same section.
3. A `bg` on a features section.
4. `parallax` on sections shorter than one viewport height.
5. Glow-layer sliced (`sliced-ocean` / `sliced-teal` / `sliced-glow`) as standalone section bg without a card.
6. Multiple languages in one code block (e.g. HTTP verb + JSON body). Pygments emits `hl-err` tokens rendered as fuchsia.
7. Unknown fields in `callout` or `code` sections (silent drops). Most common: `callout` uses `style` not `variant`, `text` not `body`.

Number of sliced bgs, number of card wrappers, and bracket placement are composition choices left to the author's judgment. They are not validator-enforced.

### Field names (silent field-drops have caused bugs)

The renderer ignores unknown fields silently. Use exact names:

| Section | Required fields | Notes |
|---------|----------------|-------|
| `hero` | `eyebrow`, `headline`, `subtitle` | CTAs come from top-level `cta_buttons`. |
| `text` | `title`, `body` | `body` is an **array** of paragraph strings. |
| `features` | `title`, `items` | `items` is `[{icon, title, desc}, ...]`. |
| `callout` | `title`, `text` | Field is `text` (NOT `body`). Field is `style: "info\|warn\|danger\|success"` (NOT `variant`). |
| `code` | `code`, `language` | `language` must be a valid pygments lexer. Do NOT mix languages in one block (e.g. HTTP + JSON) — pygments errors render as fuchsia text. |
| `cta` | `headline`, `subtitle` | Buttons come from top-level `cta_buttons`. |

### Quick example

```json
{
  "title": "Platform Overview",
  "effects": {"glow": true, "reveal_bg": true},
  "sections": [
    {"type": "hero", "eyebrow": "SignalWire", "headline": "...", "subtitle": "...", "bg": "sliced-drip"},
    {"type": "features", "title": "Why it matters", "card": "blast", "blast_color": "247,42,114", "items": [...]},
    {"type": "text", "title": "How it works", "body": ["...", "..."]},
    {"type": "features", "title": "Capabilities", "tilt": true, "items": [...]},
    {"type": "callout", "style": "info", "title": "Note", "text": "..."},
    {"type": "cta", "headline": "Get started", "subtitle": "...", "bg": "sliced-drip-up", "magnetic": true}
  ]
}
```

Hero and CTA bracket the page with the same nebula family. The thesis gets the only card wrapper. Middle features section uses tilt for interactivity without a card. Callout is the second emphasis. No textures compete in the middle.

---

## Slides

Build branded SignalWire slide decks from Python. 29 layouts, DTCG design tokens, auto-fit text, dark and light themes.

## Quick start

```python
from signalwire_slides import *

prs = Presentation()
prs.slide_width = SW; prs.slide_height = SH
bk = prs.slide_layouts[6]
lw = svg_to_png(LOGO_WHITE_SVG, width=800)

S = lambda: SlideBuilder(prs, bk, GRAD, lw)

# Title
S().title("Platform Overview", large=True, top=2.0).subtitle("Name | Role | Date").done()

# Content (most common)
s = S(); s.title("~1200ms avg conversational latency")
s.body(["1100-1500ms typical, 800ms with optimized stack", "AI at $0.16/min", "2000+ customers"])
s.done()

# KPI
S().kpi("2.7B", "Minutes processed annually").done()

# Use a preset layout
from signalwire_slides import L13_case_study
L13_case_study(prs, bk, GRAD, lw)

prs.save("my_deck.pptx")
```

## CLI

```bash
signalwire-slides                    # both themes, current directory
signalwire-slides --theme dark       # dark only
signalwire-slides -o /tmp            # output to /tmp
```

## 29 layouts

| # | Name | Use for |
|---|------|---------|
| L01 | Title | Opening slide |
| L02 | Section | Section divider |
| L03 | Agenda | Numbered topic list |
| L04 | Content | Assertion headline + 3 bullets |
| L05 | Two-Column | Side-by-side comparison |
| L06 | Image + Text | Visual + supporting text |
| L07 | Quote | Customer testimonial |
| L08 | Chart | Data with assertion headline |
| L09 | Table | Structured data comparison |
| L10 | Process | 4-step chevron flow |
| L11 | Timeline | Horizontal roadmap |
| L12 | KPI | Single big stat |
| L13 | Case Study | Challenge / Solution / Result |
| L14 | Architecture | Diagram placeholder |
| L15 | Closing | Next steps + contact |
| L16 | Appendix | Reference material |
| L21 | Bento Grid | Multi-fact dashboard |
| L22 | Before / After | Problem vs solution |
| L23 | Logo Wall | Customer logos |
| L24 | Metric Dashboard | 4 KPI cards |
| L25 | Full-Bleed Image | Photo with overlay |
| L26 | Team | Photo + name + title |
| L27 | Thesis | Bold claim + 2 proof blocks |
| L28 | Why Now | Market inflection |
| L29 | Competitive | Narrative comparison matrix |
| L30 | How It Works | Layered systems diagram |
| L17/L18 | Blank | Dark/light blank |
| L19 | Icon Reference | Brand icon grid |
| L20 | Color Reference | Brand color swatches |

## Auto-fit

Text automatically shrinks if too wide for its container. Warnings print when shrinking occurs so you know to tighten the copy.

## License

MIT
