dark_mode.go RawHistoryFolder

1package webutil
2
3import "fmt"
4
5const darkModeScriptTemplate = `(function() {
6  var prefersDark = %t;
7  window.%s = prefersDark;
8  var originalMatchMedia = window.matchMedia.bind(window);
9
10  if (prefersDark) {
11    document.documentElement.classList.add('dark');
12    document.documentElement.classList.remove('light');
13  } else {
14    document.documentElement.classList.add('light');
15    document.documentElement.classList.remove('dark');
16  }
17
18  function isColorSchemeQuery(query, scheme) {
19    if (typeof query !== 'string') return false;
20    var normalized = query.replace(/\s+/g, '').toLowerCase();
21    return normalized.indexOf('prefers-color-scheme:' + scheme) !== -1;
22  }
23
24  function createFakeMediaQueryList(query, matches) {
25    var listeners = [];
26    var onchangeHandler = null;
27    return {
28      matches: matches,
29      media: query,
30      get onchange() { return onchangeHandler; },
31      set onchange(fn) { onchangeHandler = fn; },
32      addListener: function(cb) { if (typeof cb === 'function') listeners.push(cb); },
33      removeListener: function(cb) { var idx = listeners.indexOf(cb); if (idx !== -1) listeners.splice(idx, 1); },
34      addEventListener: function(type, cb) { if (type === 'change' && typeof cb === 'function') listeners.push(cb); },
35      removeEventListener: function(type, cb) {
36        if (type === 'change') { var idx = listeners.indexOf(cb); if (idx !== -1) listeners.splice(idx, 1); }
37      },
38      dispatchEvent: function(event) {
39        for (var i = 0; i < listeners.length; i++) { try { listeners[i](event); } catch (e) {} }
40        if (onchangeHandler) { try { onchangeHandler(event); } catch (e) {} }
41        return true;
42      }
43    };
44  }
45
46  window.matchMedia = function(query) {
47    if (isColorSchemeQuery(query, 'dark')) return createFakeMediaQueryList(query, prefersDark);
48    if (isColorSchemeQuery(query, 'light')) return createFakeMediaQueryList(query, !prefersDark);
49    return originalMatchMedia(query);
50  };
51})();`
52
53// DarkModeScript returns JS that patches matchMedia and sets dark/light
54// classes on <html>. The globalVar parameter names the window property
55// that stores the preference (engine-specific for debuggability).
56func DarkModeScript(prefersDark bool, globalVar string) string {
57	return fmt.Sprintf(darkModeScriptTemplate, prefersDark, globalVar)
58}

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 8

Tags 42

↑↓ moveenter openesc close