# Changelog

All notable changes to **OpenFresco** are documented here. The format
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this
project adheres to [Semantic Versioning](https://semver.org/).

## [0.1.0] — 2026-07-22

Initial release. Server-side OG-image / social-card rendering built to run on
[Fresco](https://hex.pm/packages/fresco) canvases — a scene model that renders
to SVG or PNG, browser-free.

### Scene model & templating

- **`OpenFresco.Scene`** — a versioned, fixed-size canvas plus z-ordered
  elements: `text`, `image`, `shape`, `button` (solid/outline/soft presets),
  and `stamp` (preset text). Serializes to/from JSON (`to_json!` /
  `from_json!`) and migrates forward (`migrate/1`). Standalone at render time
  (only runtime dep is `:jason`) but designed to be carried inside a Fresco
  canvas's `extensions["open_fresco"]` blob.
- **Fills** — `solid/1`, `gradient/2` (angle + multi-stop, per-stop alpha),
  `image_fill/3` (cover/contain/stretch, optional overlay tint). Gradient
  alpha **masks** on images.
- **Templating** — `{{slot}}` / `[[global]]` inline substitution
  (`OpenFresco.Substitute`), byte-compatible with `phoenix_kit_og`. Unknown
  tokens pass through visibly; `Substitute.slots/1` lists a scene's slots with
  inferred types. Whole-value `%{placeholder: k}` shorthand supported. Locale
  is a render input.

### Rendering

- **SVG** — `OpenFresco.render_svg/3`. Pure, deterministic, XML-escaped,
  dimension-clamped (OOM guard). A missing image slot draws a neutral
  stand-in (never a `data:svg` black square).
- **PNG** — `OpenFresco.render/3` (→ `OpenFresco.Renderer`). Browser-free,
  never raises, no network. Backed by the **optional `:resvg`** dependency
  with a runtime fallback chain (`resvg` NIF → `resvg` CLI → `rsvg-convert` →
  ImageMagick → `{:error, :rasterizer_missing}`). Returns
  `{:ok, png, %{width, height}}`; `version/0` folds a cache-key token.

### Layout

- **Measurement-accurate text** (`OpenFresco.Measure`) — widths come from
  resvg's own engine when available, so wrap points match the render;
  character-estimate fallback otherwise.
- **`OpenFresco.Layout`** — word wrap, `valign`, **element-to-element
  anchoring + reflow** (chains resolve, cycles rejected), **button
  auto-width**, and legibility **underlays** (glow behind text, box behind
  shapes/images).
- **Responsive sizing** — `Scene.place/1` (canvas-relative inset placement)
  + `Scene.put_size/3` + **`OpenFresco.Sizes`** presets
  (`:og` / `:twitter` / `:square` / `:pinterest` / `:story`). One template
  adapts to any canvas size.

### Editor & migration

- **`OpenFresco.Editor.Ops`** — pure, server-authoritative editing core
  (hit-test / move / resize / reorder / delete).
- **`OpenFresco.Editor`** — a `Phoenix.LiveComponent` (optional
  `:phoenix_live_view`) rendering a server-authoritative SVG preview, with a
  JS hook in `priv/static/open_fresco.js`. _The mutation core is unit tested;
  the interactive component + hook are not yet browser-verified._
- **`OpenFresco.OgImport.scene_from_canvas/1`** — converts a `phoenix_kit_og`
  canvas map into a scene; tokens are preserved verbatim, so imported
  templates render the same output.
