All notable changes to this project will be documented in this file.
0.3.1 - 2026-08-11
Added
A conformance test for the
:phoenix_kitrequirement (#6). The pin is a contract with consumers and nothing here exercised it: a host depending on both this module and a core version the requirement excludes gets an unsolvable dependency set andmix deps.getfails outright, while this repo's own suite stays green. The specific trap is the three-segment form —~> 2.0.xexpands to< 2.1.0, so no core 2.1 satisfies it.The check reads the resolved dep first and falls back to the committed literal in
mix.exs, so it stays meaningful under thePHOENIX_KIT_PATHoverride while still failing when apath:dep is genuinely committed.
0.3.0 - 2026-08-10
Changed
⚠️ Requires
phoenix_kit ~> 2.0. The core pin moved to~> 2.0, so this release no longer resolves against core 1.7.Core 2.0.0 squashes the migration chain into a single
V135baseline and makes V135 the chain's floor:mix ecto.migratenow refuses on a database below it rather than migrating. Checkmix phoenix_kit.statusbefore upgrading. A host below V135 must installphoenix_kit 1.7.236— the migration bridge, the last release carrying the full pre-squash chain — migrate until the reported version is at least V135, and only then move to 2.0.This package does not call migration internals, so the change is the pin itself.
Added
- Any module can render an OG image (PR #5).
PhoenixKitOG.og_image_url/5is now the generic entry point; the previous publishing-only coupling lived in the call shape, not in anything essential. Scene storage plus the OpenFresco editor and renderer sit behind it.
Changed
mix precommitpasses again. It had been failing onmain— 22 credo findings, unchanged since well before this release. Cleared all 22: missing aliases for nested modules, six too-deeply-nested function bodies extracted into named helpers, and four over-complex dispatch functions inSceneEdit(update_element/4,update_canvas/3,set_anchor/4,insert/2) re-shaped from widecond/caseblocks into function clauses over the same keys. All behaviour-preserving;scene_edit_test.exspasses throughout.- Clearing credo let dialyzer run for the first time (
quality.cihalts at the first failure). Six findings surfaced, none a defect — two are artifacts of the optional PNG rasterizer backend being absent in this package's own environment, four are defensive clauses dialyzer can prove dead from today's callers. Recorded in a new.dialyzer_ignore.exs, each with its reason. - The optional calls into
phoenix_kit_publishinguseapply/3so dialyzer does not reportunknown_functionfor a peer that is not a dependency. The runtime guards (Code.ensure_loaded?+function_exported?+rescue) are unchanged and remain the real contract.
0.2.1 - 2026-07-20
Fixed
- The Hex package's
files:list never includedpriv/, so the 0.2.0 package shipped withoutpriv/static/assets/phoenix_kit_og.js(the editor JS hooks bundle declared byjs_sources/0) orpriv/gettext/— any host installing from Hex (rather than a path/git dep) failed to compile withjs_sources/0 bundle not found.privis now included in the package.
0.2.0 - 2026-07-20
Added
- Own
PhoenixKitOG.Gettextbackend +priv/gettexttranslations across 7 locales for the common UI string set (editor long-tail strings ride as English fallback pending a translation pass) - Editor JS hooks (drag/resize + keyboard) now ship via
js_sources/0as a prebuilt bundle (priv/static/assets/phoenix_kit_og.js) instead of an inline<script>— the inline script only ran on a hard page load, so navigating into the editor from the Templates list left drag/resize unregistered - On-disk render cache eviction — TTL + count cap, configurable via
:cache_ttl_seconds/:cache_max_files/:cache_prune_probability Variables.global_label/1andVariables.global_description/1— the canonical translated label/description for each OG-owned global variable- Failed create/update/delete actions now log a
failed: trueactivity row (previously only successes were audited); a failed update/delete keeps itsresource_uuidso the row still points at which record was targeted
Changed
- Preview rendering (editor + assignments modal) now runs off the LiveView
process via
start_async/cancel_async, with a loading state — a synchronous rasterize could block the whole modal for up to the 5s backend timeout
Fixed
- SVG injection — every interpolated geometry attribute and the glow-filter id are now escaped/sanitized; a crafted canvas can no longer break out of an attribute or inject markup
- Canvas
width/heightare clamped (@max_dim4000) on both the SVG viewBox and the rasterizer's output buffer, preventing an oversized canvas from making the rasterizer allocate an unbounded pixel buffer - Image hrefs resolving to
file://are dropped instead of passed through — was a local-file-read primitive via a CLI rasterizer backend Schemas.Assignmentnow declaresunique_constraint/3on its two partial unique indexes, so a concurrent double-save returns{:error, changeset}instead of raisingEcto.ConstraintErrorand crashing the LiveView- Served OG images now send
X-Content-Type-Options: nosniff - A preview render that crashes or is superseded mid-flight no longer leaks an internal error tag into the flash message text
0.1.1 - 2026-07-04
Fixed
resvgis now an optional dependency instead of required. Everyresvgrelease on Hex (up to 0.5.0, the latest) hard-pinsrustler_precompiled ~> 0.8.1, which could makephoenix_kit_ogun-installable for a host app that already needs a newerrustler_precompiledfor something else — version solving would fail with no way for the host app to work around it.Render.Rasterizeralready falls back to theresvgCLI,rsvg-convert, or ImageMagick when the NIF isn't compiled in, so making it optional loses nothing for hosts that can't take the pin; add{:resvg, "~> 0.5"}directly in the host app to opt into the NIF fast path.
0.1.0 - 2026-07-04
Added
- WYSIWYG SVG canvas editor for OpenGraph image templates — text, image, rect,
and stamp elements, with
{{slot}}(consumer-wired) and[[global]](site_url, site_host, site_name, page_url, page_locale) variable syntax - Hierarchical template assignment system (e.g.
post → group → default); admin modal for CRUD plus live preview against a real published resource - SVG → PNG rendering pipeline (
Render.render_url/2): prefers the:resvgNIF, falls back to theresvgCLI,rsvg-convert, or ImageMagick; disk cache keyed by a SHA-256 of(template, canvas, values, module_key) refine_og/4integration seam for consumer modules — kill-switch viaenabled?/0, pass-through on any resolution error or missing template so a public page render can never crash on OG renderingpreview_og_image_url/3for consumer editors to show "what the plugin will produce" without swapping the live OG imageGET /phoenix_kit/og-image/:keyimage controller —image/pngwithout a charset suffix (Telegram drops previews on binary MIME with a text charset), 30-day immutable cache headers, configurable cache directory- Consumer opt-in via two callbacks on the consumer's
PhoenixKit.Moduleimplementation:og_variables/0(declares available variables) andog_resolve/2(fetches values at render time); first consumer wired up isphoenix_kit_publishing - Activity logging for template and assignment CRUD
- Admin dashboard integration: OpenGraph overview tab plus Templates and Assignments subtabs
phoenix_kit_og_templatesandphoenix_kit_og_assignmentsschemas (migration V154), with a partial-unique-index pair so Postgres NULLscope_uuid(module-wide default) and per-scope assignments don't collide
Fixed
- The template editor's
/newroute no longer leaks an orphaned template row on every fresh page load — creation is now gated onconnected?/1since LiveView mounts twice (disconnected + connected) for a full page load Render.Svgno longer hardcodeshttp://localhost:4000for host-relative image sources (e.g. the signed local-storage fallback URL); it now degrades the same way any other unresolvable image href does