Packages

Server-side OG-image / social-card rendering, built on Fresco. A scene model (text, image, shape, button) with fills/gradients, {{slot}} templating, measured word-wrap, anchoring, and responsive sizing — rendered to SVG or PNG (via the optional resvg dependency).

Current section

Files

Jump to
open_fresco CHANGELOG.md
Raw

CHANGELOG.md

# 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.