Commit Graph
9 Commits
Author SHA1 Message Date
tanshu 25ef94d7af Use the old static menu directory as the app's data root
That was the intent of the uncommitted edit, and it is a better location than a
second directory: the app inherits the place the menus were already served from
and only ever reads and writes its own data/ and site.env inside it.

app_root is written as /var/lib/{{ host }}, quoted, because the unquoted form is
what failed to parse. Every documented path follows, and the deployment readme
now says that emptying the old menu files means deleting those files rather than
the directory, which holds the live content.
2026-09-22 23:38:21 +05:30
tanshu f2d8058456 Deploy in one run, with nothing older to preserve
There is no previous system kept serving, so the playbook no longer knows about
one: the retire flags, the temporary web address, the task that removed the old
nginx snippet and the task that stopped the old container are gone. One run
starts the container and points the Site's web address at it, and deploying an
earlier tag is the rollback. The vars lose vhost, retire_nginx_snippet,
retire_nginx_container and legacy_container; the Caddy role keeps only the
snippet upsert.

The deployment readme drops its staged-rollout sections and the glossary of words
that no longer exist, and the plan's sequence and the operating notes describe
the single run instead.
2026-09-22 23:27:17 +05:30
tanshu ce8079355b Note where identity lives in the plan and README 2026-09-22 23:11:48 +05:30
tanshu b13042f0db Adopt the Stitch design system
Google Stitch produced an Atelier Dining & Hospitality system for this app: warm
ivory linen and espresso for the guest surfaces, burnished gold for the single
active thing, Playfair Display for the restaurant's voice and Plus Jakarta Sans
for everything transactional, editorial rows with dot leaders, pill controls,
hairline containment instead of drop shadows. The four screens and the system
document are kept in docs/design/stitch/ as the source.

Both fonts are now self-hosted with their licences, the guest stylesheet is
rebuilt on the system, and the admin is the same system in its dark mode
(espresso workspace, obsidian pods, gold actions) by overriding the same tokens
at a higher specificity, since the per-Site theme is injected inline after the
stylesheets. The Site theme in config/ moves to the system's palette, which is
where per-Site branding belongs; the old Archivo font is dropped.
2026-09-22 23:07:38 +05:30
tanshu a29c5c4b0c Make the navigation out of the menus themselves
Variant B of the prototype (/prototype/menu-design branch): each category is a
card whose face is the first page of that menu, so the landing is built from the
restaurant's own paper instead of labels on a dark page. The row scrolls and
snaps on a phone and wraps into a grid on a desktop; the cards are drawn from
the existing tokens, not the prototype's hexes. The bar's links become filled
segments, the current one the only teal thing on screen.

Fixes the scroller's first card being clipped: a negative-margin full bleed was
fighting scroll-snap, so the row now insets its first card and snaps to the same
inset.
2026-09-22 22:39:01 +05:30
tanshu c31b73d553 Let the owner manage categories, and give the menus a shelf
Categories were baked into config/<site>.site.json, so adding one meant a
rebuild and a redeploy. They are now data: data/sections.json, seeded from the
config the first time, and managed in the admin (add, rename, reorder, remove).
A category's web address is fixed when it is created because that is what a QR
code points at, and a category cannot be removed while it still holds menus.

The landing and the top bar were the ugliest parts. The landing was a column of
hairlines with tiny meta — a broadsheet, and the paper that makes this app worth
looking at was nowhere on it. It is now a shelf: each category is a row whose
right edge is a sliver of that menu's first page, bleeding off the screen, with
the shelf edge in brass and the name in heavy tight Archivo. The bar was a row
of teal pills that would not survive more categories: it now names the menu you
are reading and opens a sheet listing the rest, each with its own paper sliver,
and the current one is the only teal thing on screen. The sheet is a link to the
landing page, so navigating still works without the script.

Also fixes the category manager's own layout, which I shipped unstyled, and the
markup contract the route tests described.
2026-09-22 22:24:53 +05:30
tanshu 7bf67cc299 Make reordering work on the page you are actually looking at
Two defects, both found by driving the real editor in Firefox:

1. The editor drew grab handles with a grab cursor on sets it refuses to
   reorder, so the live Food menu looked draggable and did nothing. Handles and
   the move buttons are now rendered only when reordering is possible, and the
   notice says what can still be done.

2. Reordering was saved only from dragend. Browsers suppress mouse events during
   a drag and dragend is not guaranteed to arrive, which left the rows visibly
   reordered while the server kept the old order — the drag looked like it
   worked and then reverted on reload. The order now commits from dragover after
   a pause in the drag, with drop/dragend/mouse events as faster paths, deduped
   against the last confirmed order and reported in the page.

To make (1) coherent the Current set may now be reordered while its Pages stay
fixed: order is presentation, media URLs are content-hashed and pages are
rendered per request, so nothing stale can be served (ADR 0006). Archives stay
frozen. Admin pages are no-store and assets cache for 5 minutes, so a fix like
this one is not masked by a stale admin.js.

scripts/check-dnd.mjs drives the real editor and asserts the drag persists; it
is the only automated check that reaches this path.
2026-09-22 22:01:52 +05:30
tanshu 1f06e32854 Add the playbook that deploys one Site
The playbook pulls the tag from the registry, creates the data root owned by the
app user, runs the container with its config baked into the image, waits for
health, then verifies the data directory as the app user so a permissions
mistake surfaces at deploy time. Caddy is pointed at the app with a stable
marker, so a temporary hostname can be checked beside the live site and the
cutover is one invocation with the retire flags set.
2026-09-22 21:25:52 +05:30
tanshu edea83228c Menu Set store, image pipeline and Site config
The store keeps a Menu Set's state in the directory layout (drafts/ versus
published/) and its metadata in a manifest, with publishing as a single
directory rename. Editing is allowed on Drafts only, so a published menu can
never change under a customer. Stages 2 and 3 of docs/plan.md, with tests.
2026-09-22 21:15:00 +05:30