Feature: generate a printable PDF menu (with QR ideas) #1
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Background
Restaurants want a physical, printable menu they can hand customers,
laminate, tape to walls, or fold into a table tent. Today the menu
only exists in the database, on Nostr, and as live UI in the customer
webapp. There's no path from the operator's CMS to a print-ready
artifact.
A PDF generator inside the extension closes that loop. It also
becomes a useful bridge between the printed and the digital
worlds — every item in the database carries enough structure
(
name,description,price,dietary,allergens,nostr_event_id,ancestor menu-tree context) that a well-rendered PDF can encode
all of it, and QR codes on the page can deep-link printed items back
to the live, dynamic experience.
This is a brainstorm issue. We can break it into smaller deliverables
once the shape is clearer.
Why this is interesting
hiccups, the printed menu is still complete and current as of the
last print.
restaurant emails to customers, posts on social, or sends to a
print shop.
customer straight into the live ordering flow — no app install,
no typing URLs.
naddr1...pointing at akind-30402 listing so any Nostr client (not just our webapp) can
resolve and act on a printed item.
Goals
print-ready PDF of the current menu, branded for their restaurant.
prices, sold-out flags, availability windows, modifier groups.
decision (see below).
also be fetched programmatically by integrations.
Non-goals (for v1)
theme or supply their own template; that's it.
to a real print shop should expect to feed the PDF through a pro
tool.
Open design questions
1. Where does PDF generation happen?
reportlab, or fpdf2.
WeasyPrint is the obvious pick: it consumes HTML+CSS, so the same
Jinja templates that drive the cms can be the source of
truth for layout. New paths just produce alternate stylesheets.
bigger dependency.
every consumer has to render — the operator clicks "Download" and
the browser does the work.
Recommendation to validate: WeasyPrint server-side, with a Jinja
template at
templates/restaurant/menu_print.htmland an adminendpoint
GET /api/v1/restaurants/{id}/menu.pdf?theme=<name>thatstreams
application/pdf. Keeps the source of truth on the serverand matches LNbits's existing template conventions.
2. What does the layout look like?
A first cut:
tagline, location, social links, header QR.
anywhere]]).
leader to the price.
hints ("with chicken +5 / tofu +3").
on Nostr), generation timestamp, page numbers.
Open: which preset themes to ship? Modern minimal, rustic warm,
classic monochrome? My instinct: ship one solid default, plus a
single CSS hook for operator overrides.
3. Currency / multi-currency
Items have their own
currencyfield. The PDF should:50 GTQ,1500 sat, etc.).LNbits's
fiat_amount_as_satoshisplumbing.settings panel (display in declared currency only, declared +
converted, sat only, etc.).
4. QR codes — three flavors, all worth shipping
We can do all three; they aren't mutually exclusive. They're listed
roughly in increasing complexity so we know what to land first.
a) Restaurant-level QRs (cover / footer)
customer webapp's restaurant page, e.g.
https://atitlan.io/r/<slug>. Tap to enter the live menu.WIFI:T:WPA;S:<ssid>;P:<password>;;) usingcredentials from a new (optional)
restaurant.wifi_*field. Lotsof cafés already use these on table tents.
pubkey as
nostr:npub1...so a Nostr-native client can followthe restaurant directly.
These are all once-per-PDF and don't require any per-item tracking.
b) Section / category QRs
webapp at that branch of the menu, e.g.
https://atitlan.io/r/<slug>#node=<id>. Useful when the printedmenu is a tease (limited space) and the digital one has the full
range.
client without our webapp can still parse the link.
Medium effort. Mostly a layout decision (where to put them so they
don't dominate the page).
c) Per-item QRs (the Nostr-native one)
nostr:naddr1...— a NIP-19 addressable pointer to thekind-30402 nostr-layer, so any Nostr
client can resolve and act. This is the most
protocol-pure option — works without our webapp, works
offline-of-our-server, works in the future once more clients
exist.
https://atitlan.io/r/<slug>/ add?item=<id>) that loads the item into the webapp cartpre-selected.
Heaviest visually — more QR codes per page. Worth doing for the
"scan to order" use case (kiosk-less ordering, scan-to-pay flows
on tables) and for the protocol story.
A toggle in the settings panel chooses which flavor of per-item QR
the PDF emits (or off).
5. Generation pipeline
Two flavors:
synchronously and stream it. Works fine for restaurants in the
5–50 item range.
cache the result, serve cached. Worth it if PDFs end up bigger
or if we want to publish a NIP-94
file metadata event referencing a permanent URL for the PDF —
i.e. the printed menu itself becomes a Nostr-discoverable
artifact.
Recommendation: ship on-demand first. Add caching + NIP-94 publish
as a second issue once we know the generator is solid.
6. Versioning + provenance
A printed menu has a shelf life. Helpful additions:
timezone.
next to the timestamp, so the operator can tell two printed
versions apart at a glance.
menu version (NIP-94 event with file URL + content hash). Nice
to have, not v1.
7. Accessibility / readability
Quasar-via-CDN system stack). Pull a free print-quality face
(e.g. Inter, Source Sans, IBM Plex) and embed it.
screen-reader / OCR friendliness.
Stretch goals
using item description i18n (when we add it).
unique QR encoding
?table=<n>so orders carry table metadata.upload it to Blossom / a self-hosted file host and publish a
NIP-94 event so the menu artifact is part of the restaurant's
Nostr presence.
is_featured=trueor items in the lunch availability window.next generation of phones.
Acceptance criteria for v1
(Subject to the design decisions above being settled. Listed so we
have a checkpoint when we break this issue down.)
GET /api/v1/restaurants/{id}/menu.pdfreturns aapplication/pdfstream, gated by the admin key.custom CSS string in restaurant settings.
cap), renders items at any level, respects sold-out + dietary
+ allergen + modifier hints + currency.
Nostr profile) lands in v1. Section + per-item QRs can be
follow-up issues if they balloon.
hash.
pdf-exportnote describing thepipeline + theme system.
Files / surfaces touched (rough)
views_api.py— new routeservices.py(or newpdf.py) — generation pipelinetemplates/restaurant/menu_print.html— print stylesheetstatic/css/print/*.css— preset themesstatic/js/menu.js— Download buttonmodels.py/migrations.py— possibly new restaurant fields(
wifi_ssid,wifi_password,print_theme)pyproject.toml—weasyprint(or chosen lib) +qrcodedocs/pdf-export.md(new) + cross-link fromdocs/index.md,docs/cms.md,docs/architecture.mdReferences
naddr1...): https://github.com/nostr-protocol/nips/blob/master/19.md