detached 27e453c

sw.js RawHistoryFolder

1// sw.js: the service worker of the installed app, served at /-/sw.js with
2// Service-Worker-Allowed: / (see serviceworker.go).
3//
4// Browsers want an installed app to have a service worker with a fetch handler
5// (Chrome on Android mints a WebAPK for such sites, and drops an empty handler
6// as if there were none). A forge has nothing to cache: pages are rendered per
7// caller (the owner sees private repositories), diffs and logs are live. So
8// this worker stores nothing. A page navigation goes to the network as it would
9// without the worker; only when the network is unreachable is the visitor shown
10// a short offline page instead of the browser's error. Every other request
11// (assets, the event stream, Git, POSTs) is left to the browser: the handler
12// does not answer it.
13//
14// The browser fetches this file again on each navigation (at least once a day)
15// and installs the new version when a single byte differs; edit it to ship a
16// new version. The worker takes over at once (skipWaiting, claim): it holds no
17// state.
18'use strict';
19
20const OFFLINE = `<!doctype html>
21<html lang="en"><head><meta charset="utf-8">
22<meta name="viewport" content="width=device-width, initial-scale=1">
23<meta name="color-scheme" content="light dark">
24<title>ermite is unreachable</title></head>
25<body style="font: 16px/1.5 system-ui, sans-serif; max-width: 32rem; margin: 20vh auto; padding: 0 1rem">
26<h1 style="font-size: 1.25rem">ermite is unreachable</h1>
27<p>The server does not answer. Check the connection, then reload.</p>
28</body></html>`;
29
30self.addEventListener('install', () => self.skipWaiting());
31
32self.addEventListener('activate', (event) => {
33  // Navigation preload starts the page request while the worker boots, so the
34  // worker adds no latency to a navigation.
35  event.waitUntil(Promise.all([
36    self.registration.navigationPreload?.enable(),
37    self.clients.claim(),
38  ]));
39});
40
41self.addEventListener('fetch', (event) => {
42  const req = event.request;
43  if (req.mode !== 'navigate' || req.method !== 'GET') return;
44  event.respondWith((async () => {
45    try {
46      return (await event.preloadResponse) || (await fetch(req));
47    } catch {
48      return new Response(OFFLINE, {
49        status: 503,
50        headers: { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-store' },
51      });
52    }
53  })());
54});

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 4

Tags 45

↑↓ moveenter openesc close