- commit
- addbbb83bc9fad4c91782bf5641aeb8344b5428b
- parent
- 0ace0e42789227359ff22b970516432fed6d70c1
- author
- Dami <dami.etoile@gmail.com>
| A | internal/ | bin | |
| M | internal/ | 7 | +++++-- |
| A | internal/ | bin | |
| A | internal/ | bin | |
| A | internal/ | bin | |
| A | internal/ | 1 | + |
| M | internal/ | 17 | +++++++++++++++-- |
| M | internal/ | 10 | ++++++++-- |
| M | internal/ | 4 | ++-- |
| M | internal/ | 26 | +++++++++++++++++++++----- |
| M | internal/ | 12 | ++++++++++++ |
| M | internal/ | 15 | +++++++++++++++ |
Binary file.
@@ -66,8 +66,11 @@
6666 // in step with the rail colour of whichever theme is active.6767 const paintChrome = () => {6868 const rail = getComputedStyle(root_).getPropertyValue("--rail").trim();69- const themeMeta = document.querySelector('meta[name="theme-color"]');70- if (themeMeta && rail) themeMeta.content = rail;69+ if (rail) document.querySelectorAll('meta[name="theme-color"]').forEach(meta => { meta.content = rail; });70+ // The manifest is fetched at install and on later update checks, so71+ // point it at the variant whose launch colours match the active theme.72+ const manifest = document.querySelector('link[rel="manifest"]');73+ if (manifest) manifest.href = localPath("/manifest.webmanifest") + (currentTheme() === "dark" ? "?theme=dark" : "");7174 };7275 paintChrome();7376 matchMedia("(prefers-color-scheme: dark)").addEventListener("change", paintChrome);Binary file.
Binary file.
Binary file.
@@ -1,14 +1,27 @@
11 {2+ "id": "BASE/",23 "name": "NAME",34 "short_name": "NAME",45 "description": "A tiny Nostr relay",56 "start_url": "BASE/",67 "scope": "BASE/",78 "display": "standalone",8- "background_color": "#fbfaf7",9- "theme_color": "#f4f2ec",9+ "display_override": ["standalone", "minimal-ui"],10+ "launch_handler": {"client_mode": "navigate-existing"},11+ "background_color": "BACKGROUND",12+ "theme_color": "THEME",13+ "categories": ["social", "utilities"],14+ "prefer_related_applications": false,1015 "icons": [16+ {"src": "BASE/icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any"},17+ {"src": "BASE/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any"},18+ {"src": "BASE/icon-maskable-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable"},1119 {"src": "BASE/icon.svg", "sizes": "any", "type": "image/svg+xml", "purpose": "any"},1220 {"src": "BASE/icon-mono.svg", "sizes": "any", "type": "image/svg+xml", "purpose": "monochrome"}21+ ],22+ "shortcuts": [23+ {"name": "Files", "url": "BASE/files", "icons": [{"src": "BASE/icon-192.png", "sizes": "192x192"}]},24+ {"name": "Repositories", "url": "BASE/repos", "icons": [{"src": "BASE/icon-192.png", "sizes": "192x192"}]},25+ {"name": "Manage", "url": "BASE/manage", "icons": [{"src": "BASE/icon-192.png", "sizes": "192x192"}]}1326 ]1427 }@@ -2,12 +2,18 @@
22 <html lang="en" data-js="no">33 <head>44 <meta charset="utf-8">5-<meta name="viewport" content="width=device-width,initial-scale=1">5+<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">66 <meta name="color-scheme" content="light dark">7-<meta name="theme-color" content="#f4f2ec">7+<meta name="theme-color" content="#f4f2ec" media="(prefers-color-scheme: light)">8+<meta name="theme-color" content="#1e1d1a" media="(prefers-color-scheme: dark)">9+<meta name="mobile-web-app-capable" content="yes">10+<meta name="apple-mobile-web-app-capable" content="yes">11+<meta name="apple-mobile-web-app-status-bar-style" content="default">12+<meta name="apple-mobile-web-app-title" content="{{.Slug}}">813 <title>{{.Title}}</title>914 <link rel="manifest" href="/manifest.webmanifest">1015 <link rel="icon" href="/icon.svg" type="image/svg+xml">16+<link rel="apple-touch-icon" href="/apple-touch-icon.png">1117 <style>{{stylesheet}}</style>1218 <script src="/fixi.js"></script>1319 <script src="/signer.js"></script>@@ -24,13 +24,13 @@ img{max-width:100%}
2424 2525 /* Shell: a table with three columns; each column scrolls on its own. */2626 #topbar{display:none}27-#layout{border-collapse:collapse;table-layout:fixed;width:100%;height:calc(100vh - 28px)}27+#layout{border-collapse:collapse;table-layout:fixed;width:100%;height:calc(100vh - 28px - env(safe-area-inset-bottom,0px))}2828 #layout>tbody>tr>td{vertical-align:top;padding:0;position:relative;height:100%}2929 #railbox,#content,#panel{position:absolute;top:0;right:0;bottom:0;left:0;overflow:auto;overscroll-behavior:contain}3030 #rail{width:240px;background:var(--rail);border-right:1px solid var(--line)}3131 #aside{width:280px;border-left:1px solid var(--line)}3232 #content>section{padding:14px 22px 30px}33-#footer{position:fixed;left:0;right:0;bottom:0;height:28px;border-top:1px solid var(--line);background:var(--rail);font:13px var(--mono);padding:0 2ch 0 2.5ch;box-sizing:border-box}33+#footer{position:fixed;left:0;right:0;bottom:0;height:calc(28px + env(safe-area-inset-bottom,0px));border-top:1px solid var(--line);background:var(--rail);font:13px var(--mono);padding:0 calc(2ch + env(safe-area-inset-right,0px)) env(safe-area-inset-bottom,0px) calc(2.5ch + env(safe-area-inset-left,0px));box-sizing:border-box}3434 #footer table{width:100%;border-collapse:collapse;height:27px}#footer td{border:0;padding:0;vertical-align:middle}3535 #footer td:last-child{text-align:right}3636 #prompt{color:var(--muted)}#prompt b{color:var(--ink);font-weight:500}@@ -1,9 +1,25 @@
11 // Service worker: makes the relay installable and keeps navigation working2-// when the network fails by serving the last shell seen. Relay data is never3-// cached; every request goes to the network first.2+// when the network fails. Every request goes to the network first. Successful3+// page loads and the shell scripts are kept as the offline fallback; relay4+// data requested by scripts is never cached.5+const CACHE = "tiny-shell-v1";6+const shellAsset = url => /\/(fixi\.js|signer\.js|webmcp\.js|sw\.js|icon[^/]*\.(svg|png)|apple-touch-icon\.png|manifest\.webmanifest)$/.test(url.pathname);47 self.addEventListener("install", () => self.skipWaiting());5-self.addEventListener("activate", event => event.waitUntil(self.clients.claim()));8+self.addEventListener("activate", event => {9+ event.waitUntil(caches.keys().then(keys => Promise.all(keys.filter(key => key !== CACHE).map(key => caches.delete(key)))).then(() => self.clients.claim()));10+});611 self.addEventListener("fetch", event => {7- if (event.request.method !== "GET") return;8- event.respondWith(fetch(event.request).catch(() => caches.match(event.request)));12+ const request = event.request;13+ if (request.method !== "GET") return;14+ const url = new URL(request.url);15+ if (url.origin !== location.origin) return;16+ const keep = request.mode === "navigate" || shellAsset(url);17+ if (!keep) return;18+ event.respondWith(fetch(request).then(response => {19+ if (response.ok && response.type === "basic") {20+ const copy = response.clone();21+ caches.open(CACHE).then(cache => cache.put(request, copy)).catch(() => {});22+ }23+ return response;24+ }).catch(async () => (await caches.match(request)) || (request.mode === "navigate" ? caches.match(new Request(url.origin + url.pathname.replace(/\/[^/]*$/, "/"))) : undefined)));925 });@@ -59,6 +59,18 @@ var fileWorkspaceJS string
5959 //go:embed manifest.webmanifest6060 var manifestTemplate string6161 62+//go:embed icon-192.png63+var icon192PNG []byte64+65+//go:embed icon-512.png66+var icon512PNG []byte67+68+//go:embed icon-maskable-512.png69+var iconMaskablePNG []byte70+71+//go:embed apple-touch-icon.png72+var appleTouchIconPNG []byte73+6274 //go:embed sw.js6375 var serviceWorkerJS []byte6476 @@ -199,6 +199,12 @@ func (a *App) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
199199 _, _ = writer.Write(serviceWorkerJS)200200 return201201 }202+ if png, ok := map[string][]byte{"/icon-192.png": icon192PNG, "/icon-512.png": icon512PNG, "/icon-maskable-512.png": iconMaskablePNG, "/apple-touch-icon.png": appleTouchIconPNG}[request.URL.Path]; ok {203+ writer.Header().Set("content-type", "image/png")204+ writer.Header().Set("cache-control", "public, max-age=86400")205+ _, _ = writer.Write(png)206+ return207+ }202208 if request.URL.Path == "/icon.svg" || request.URL.Path == "/icon-mono.svg" {203209 writer.Header().Set("content-type", "image/svg+xml; charset=utf-8")204210 if request.URL.Path == "/icon.svg" {@@ -730,6 +736,15 @@ func (a *App) manifest(writer http.ResponseWriter, request *http.Request) {
730736 encoded, _ := json.Marshal(name)731737 body := strings.ReplaceAll(manifestTemplate, `"NAME"`, string(encoded))732738 body = strings.ReplaceAll(body, "BASE/", requestPrefix(request)+"/")739+ // A manifest carries one set of launch colours. The page links the740+ // variant that matches the active theme so the installed app's splash741+ // screen and status bar do not flash light on a dark phone.742+ theme, background := "#f4f2ec", "#fbfaf7"743+ if request.URL.Query().Get("theme") == "dark" {744+ theme, background = "#1e1d1a", "#171614"745+ }746+ body = strings.ReplaceAll(body, "THEME", theme)747+ body = strings.ReplaceAll(body, "BACKGROUND", background)733748 writer.Header().Set("content-type", "application/manifest+json; charset=utf-8")734749 _, _ = writer.Write([]byte(body))735750 }