Files
RecipesCam/docker/frontend/index.html
T
3dtours 0f2e109aa2 web: make the studio installable, and give it a shell that opens offline
The three things a browser asks for, without a plugin: a manifest in
public/ (name, /app as the start, three icons cut from the one piece of
art this repo has), a service worker, and the two metas iOS reads
instead of the manifest.

The worker caches the shell — /, /app, /library, all one document under
the SPA fallback — and the hashed assets the build emits. A navigation
is network-first, so a deploy is never pinned behind the cache; a
hashed asset or the wasm is cache-first, because under a given build
those never change. /api and any non-GET go straight out: a worker is a
cache, not a proxy. nginx serves sw.js and manifest.json `no-cache`
(both names outlive their contents) with the isolation headers the
worker script needs under COEP.

The offer is the app's own dialog, not Chromium's mini-infobar: the
event is held, and it is spent either after the visitor has been in the
studio two minutes or the moment an export lands — the point at which
the app has done their work. Safari never fires the event, so it gets
the Share > Add to Home Screen line instead. A refusal is remembered and
never asked again.

  node scripts/make-icons.mjs       192x192 39785B / 512x512 159296B / maskable 512x512 123723B
  node scripts/pwa-check.mjs        manifest 3 icons · worker activated · shell cached
                                    · offline reload of /app paints
  off (https://localhost:8090)      same four, through nginx
2026-09-28 17:46:08 +07:00

50 lines
3.2 KiB
HTML

<!doctype html>
<html lang="vi" data-theme="dark" data-accent="amber" data-fonts="studio">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<meta name="theme-color" content="#0a0a0a" />
<link rel="icon" type="image/png" href="/assets/RecipesCamIcon.png" />
<link rel="apple-touch-icon" href="/assets/RecipesCamIcon.png" />
<!-- The installed app. The manifest is a static file in public/ (see
public/manifest.json and scripts/make-icons.mjs for its icons), so a build
needs no plugin to ship it. iOS ignores most of the manifest and reads these
three metas instead: 'capable' is what makes a home-screen bookmark open
without Safari's chrome, 'black-translucent' gives it the app's black bars,
and 'title' is the name under the icon. -->
<link rel="manifest" href="/manifest.json" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="apple-mobile-web-app-title" content="RecipesCam" />
<title>RecipesCam — web</title>
<meta name="description" content="RecipesCam web — grade photos with the same film recipes as the app." />
<link rel="canonical" href="/" />
<!-- Share card. The URLs are relative on purpose: a crawler resolves them
against whatever host serves the page, so the same build previews
correctly on any deployment. og:image is the 1200x630 hero shot in
public/assets/og-cover.png (see the og-cover script in the repo notes). -->
<meta property="og:type" content="website" />
<meta property="og:site_name" content="RecipesCam" />
<meta property="og:title" content="RecipesCam — Color Recipes & Film Camera Studio" />
<meta property="og:description" content="Grade ảnh bằng công thức màu phim Kodak & Fuji — tone curve, hạt phim và halation. Cùng một pipeline trên điện thoại và web studio, ảnh không rời khỏi máy bạn." />
<meta property="og:url" content="/" />
<meta property="og:image" content="/assets/og-cover.png" />
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:image:alt" content="RecipesCam landing page — capture and grade photos with exclusive color recipes" />
<meta property="og:locale" content="vi_VN" />
<meta property="og:locale:alternate" content="en_US" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="RecipesCam — Color Recipes & Film Camera Studio" />
<meta name="twitter:description" content="Grade ảnh bằng công thức màu phim Kodak & Fuji — tone curve, hạt phim và halation. Cùng một pipeline trên điện thoại và web studio." />
<meta name="twitter:image" content="/assets/og-cover.png" />
<meta name="twitter:image:alt" content="RecipesCam landing page — capture and grade photos with exclusive color recipes" />
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>