Skip to content

Pluggable recipe card designs - #65

Merged
gosku merged 15 commits into
mainfrom
more-card-designs
Jul 26, 2026
Merged

Pluggable recipe card designs#65
gosku merged 15 commits into
mainfrom
more-card-designs

Conversation

@gosku

@gosku gosku commented Jul 26, 2026

Copy link
Copy Markdown
Owner

Recipe cards used to be a single square layout with a handful of config toggles. This PR turns card rendering into a set of pluggable designs and adds two new ones built for full-screen sharing.

A card is now produced by a CardDesign (an abc.ABC): each design owns its own composition logic and canvas size, while the shared concerns (the QR code and its spec, the logo, EXIF embedding, file saving) stay common so every card scans and imports the same way regardless of how it looks. The four legacy templates collapse into a single ClassicDesign that keeps their persisted identifiers, so existing cards and the recipe.card.created events are unaffected.

Two new portrait designs (1080×1920, 9:16, sized for Instagram Stories/Reels) are included:

  • Aperture: a dark, frosted-glass card with a blurred hero photo and tiled parameters.
2026-07-26_13-32
  • Contact Sheet: a light "paper" spec-sheet listing every parameter in two columns.
2026-07-26_13-34

The create-card modal gains htmx tabs, so a user picks a design and sees only that design's options with a live preview. The two photo-centric designs are built around a real example photo and require a background image; Classic keeps the gradient fallback.

Around the feature, previews are made cheaper (composed once, sourced from the cached gallery thumbnail, and no longer served stale from the browser cache), the Aperture darkening gradient is configurable through settings, and the web-interface docs are restructured to walk through the whole recipe workflow, including the new designs.

Rationale and the trade-offs (notably the choice of an ABC and per-design canvas sizes) are recorded in ADR 012.

gosku and others added 15 commits July 25, 2026 15:38
Move the design-agnostic Pillow helpers (cover_fill, build_gradient,
load_font, make_qr, embed_recipe_exif) and the shared constants (QR size,
blur radius, gradient colours) out of the cards operations module into a
new rendering module. Introduce a RenderedCard value object for the
(canvas, json_str, embed_exif) compose result.

This is a pure extraction with no behaviour change: _compose_card now
delegates to the shared primitives and returns a RenderedCard. It sets up
the shared foundation that the upcoming pluggable card designs will reuse.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Introduce a designs/ package with a CardDesign abstract base class (the
polymorphic seam for pluggable card designs) and a ClassicDesign that
reproduces the current square-card composition. The four legacy templates
collapse into one ClassicDesign parameterized by label_style /
background_effect / info_side; its template_name property reproduces the
existing persisted strings for backward compatibility.

Decouple get_recipe_cover_lines from CardTemplate: it now takes a
label_style string, so ClassicDesign (and future designs) no longer depend
on the templates module. ClassicDesign is not yet wired into operations;
that swap happens next.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the template + info_side parameters of the card operations with a
single design: CardDesign object across the domain operations, the create/
preview/batch use cases, and the interface views. The interface now builds a
ClassicDesign from the modal's option values via _resolve_design (replacing
_resolve_card_template / _resolve_info_side); the batch use case uses a fixed
ClassicDesign. Delete the now-unused templates module and the monolithic
_compose_card (its logic lives in ClassicDesign.render).

RecipeCard.template and the recipe.card.created event still carry the same
template_name strings, so persisted rows and event history are unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add the OFL-licensed Archivo (variable) and Space Mono TTFs under
interfaces/static/fonts, and bake the stacked filmcase logo (icon + Archivo
Black wordmark, faithful to filmcase_stacked_tight.svg) to a 2x transparent
PNG in the original-branding images. Add the shared rendering helpers the new
portrait designs need: Archivo/Space Mono loaders, draw_tracked_text (Pillow
has no letter-spacing), the inline filmcase wordmark, and rounded-corner
mask / clip / paste helpers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add ApertureDesign: a dark, story-format (1080x1920) card with a blurred,
darkened photo background, a rounded hero photo, frosted-glass parameter
tiles (a Film Simulation + White Balance hero row and a 3-column grid), and
a bottom import module with the stacked logo and QR on a white pill. The QR
stays the shared 300px so every design scans the same way.

Add reusable queries the designs share: get_recipe_field_lines (format an
ordered field subset, reusing the applicability rules), is_monochromatic,
and get_sensor_names; get_recipe_cover_lines now builds on the first.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add ContactSheetDesign: a light paper spec-sheet card (1080x1920) with a
blurred photo frame behind a rounded paper panel that holds a red-accented
header, a hero photo, a two-column key/value list of every parameter with
hairline rows, and a hairline-separated import module with the logo + QR
directly on the paper. Add a shared rendering.line_height helper used by
both portrait designs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Let users pick between the Classic, Aperture, and Contact Sheet designs via
htmx tabs in the create-card modal. Each tab swaps in only that design's
options (Classic keeps label style / background effect / info side; the
photo-centric designs just describe themselves) and refreshes the live
preview. The gradient background option is hidden for the photo-required
designs, which auto-select an image.

The preview/create views and the use cases now resolve the design from a
single 'design' parameter via _resolve_design; a new RecipeCardDesignOptions
view serves each tab's controls.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add ADR 012 recording the CardDesign abstraction that supersedes ADR 005's
flat CardTemplate composition model: its flaws (one hardcoded square layout,
layout-coupled info_side, monolithic compose function, single-design modal)
and the decision taken to fix them, including per-design canvas size, shared
QR/logo/EXIF globals, the ABC choice, the photo-required rule, and the htmx
design tabs. Link it from the docs index.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The card modal's background-image picker requested images with ?w=160, but
ImageFile only honours the width parameter, so each of the 12 thumbnails
downloaded the full-size original. Use ?width=600 - the same size the image
gallery renders - so the picker reuses the already-generated, cached
thumbnails instead of triggering a fresh 160px thumbnail generation on first
open. Card creation and preview still read the full-resolution file, so
output quality is unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add RECIPE_CARD_APERTURE_SCRIM_TOP_OPACITY / _BOTTOM_OPACITY settings
(percentages, default 30 / 80) controlling the top->bottom darkening
gradient over the Aperture card background. The design reads them at render
time (not import time), so they can be tuned from the env file without code
changes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The preview <img> URL is keyed only on the options, so re-rendering the
pane with the same image/design (or after an env-driven change) reused the
browser's cached image and never refetched. Send Cache-Control: no-store on
the preview-file response and add a per-render cache-busting nonce to the
preview <img> URL, so every pane render pulls a freshly generated preview.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CardDesign.render now takes a background_photo_path instead of the Image
model, decoupling composition from persistence. Saved cards still render from
the full-resolution original; previews render from the 600px gallery
thumbnail (generated once, reused from cache), so each preview avoids
decoding and resizing the multi-megapixel source - a big speedup for the
blur-heavy Aperture/Contact Sheet designs. Preview photo quality is slightly
softer, which is fine for a small on-screen preview.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The preview pane previously rendered the card to produce its path, then the
browser loaded the <img> from the file view which rendered it a second time.
The pane now just validates the recipe/image exist and returns the <img>;
the file view is the sole renderer, composing the card lazily when the image
loads. This halves preview work per option change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Expand the recipe-card section of web_interface.md to describe the Classic,
Aperture, and Contact Sheet designs (canvas sizes, options, the shared QR,
colour/B&W field handling, and the photo requirement for the portrait
designs), link ADR 012, and add example images of the two new designs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rework docs/web_interface.md around the recipe workflow:

- Add a numbered table of contents and hierarchical section numbering
  (1, 1.1, 2.1.1, ...), with matching heading anchors.
- Move each section's screenshot directly below its title, and refresh the
  gallery / image-detail / recipe-detail / explorer screenshots.
- Split recipe import/create/delete into its own section, and restructure
  Recipe Detail around the key tools (send to camera, create recipe card,
  distribution & versions) as sub-sections.
- Document the three card designs with a single side-by-side example image,
  explain the sharing motivation, and note the open-format plan.
- Expand 'Send recipe to camera' with the connection steps and a short demo
  GIF; add the motivation and version-timeline walkthrough for
  distribution & versions, with its panel and change-group images.
- Rephrase throughout to avoid em dashes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@gosku
gosku merged commit b88b0c4 into main Jul 26, 2026
4 checks passed
@gosku
gosku deleted the more-card-designs branch July 26, 2026 03:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant