v0.4.0 6203a22

toast_js_cgo.go RawHistoryFolder

1//go:build webkit_cgo
2
3package webkit
4
5// getToastScript returns the injected toast notification component script.
6// Provides a reusable toast system that can display messages across all pages.
7func getToastScript() string {
8	return `(() => {
9  try {
10    if (window.__dumber_toast_loaded) return; // idempotent
11    window.__dumber_toast_loaded = true;
12
13    const T = {
14      container: null,
15      toasts: [], // {id, element, timeout}
16      counter: 0,
17
18      init() {
19        if (T.container) return;
20        
21        // Create toast container
22        const container = document.createElement('div');
23        container.id = 'dumber-toast-container';
24        container.style.cssText = 'position:fixed;bottom:20px;right:20px;z-index:2147483646;pointer-events:none;';
25        document.documentElement.appendChild(container);
26        T.container = container;
27
28        // Add CSS styles
29        const style = document.createElement('style');
30        style.textContent = ` +
31"`" + `
32          .dumber-toast {
33            background: rgba(18, 18, 18, 0.95);
34            color: #eee;
35            padding: 12px 16px;
36            border-radius: 8px;
37            border: 1px solid #444;
38            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
39            font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, "Helvetica Neue", Arial, sans-serif;
40            font-size: 14px;
41            line-height: 1.4;
42            margin-bottom: 8px;
43            max-width: 320px;
44            word-wrap: break-word;
45            backdrop-filter: blur(8px);
46            -webkit-backdrop-filter: blur(8px);
47            pointer-events: auto;
48            cursor: default;
49            transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
50            transform: translateX(100%);
51            opacity: 0;
52          }
53          
54          .dumber-toast.show {
55            transform: translateX(0);
56            opacity: 1;
57          }
58          
59          .dumber-toast:hover {
60            background: rgba(27, 27, 27, 0.95);
61            border-color: #555;
62          }
63        ` + "`" + `;
64        document.documentElement.appendChild(style);
65      },
66
67      show(message, duration = 2500) {
68        T.init();
69        
70        const id = ++T.counter;
71        const toast = document.createElement('div');
72        toast.className = 'dumber-toast';
73        toast.textContent = message || 'Notification';
74        toast.setAttribute('data-toast-id', id);
75        
76        // Click to dismiss
77        toast.addEventListener('click', () => T.dismiss(id));
78        
79        T.container.appendChild(toast);
80        
81        // Trigger show animation
82        requestAnimationFrame(() => {
83          toast.classList.add('show');
84        });
85        
86        // Auto-dismiss
87        const timeout = setTimeout(() => T.dismiss(id), duration);
88        
89        T.toasts.push({ id, element: toast, timeout });
90        
91        console.log('📋 Toast:', message);
92        return id;
93      },
94
95      dismiss(id) {
96        const toastIndex = T.toasts.findIndex(t => t.id === id);
97        if (toastIndex === -1) return;
98        
99        const toast = T.toasts[toastIndex];
100        clearTimeout(toast.timeout);
101        
102        // Animate out
103        toast.element.style.transform = 'translateX(100%)';
104        toast.element.style.opacity = '0';
105        
106        setTimeout(() => {
107          if (toast.element && toast.element.parentNode) {
108            toast.element.parentNode.removeChild(toast.element);
109          }
110          T.toasts.splice(toastIndex, 1);
111        }, 300);
112      },
113
114      clear() {
115        T.toasts.forEach(toast => {
116          clearTimeout(toast.timeout);
117          if (toast.element && toast.element.parentNode) {
118            toast.element.parentNode.removeChild(toast.element);
119          }
120        });
121        T.toasts = [];
122      }
123    };
124
125    // Public API for native integration
126    window.__dumber_showToast = (message, duration) => T.show(message, duration);
127    window.__dumber_dismissToast = (id) => T.dismiss(id);
128    window.__dumber_clearToasts = () => T.clear();
129
130    console.log('✅ Dumber Browser toast system loaded');
131    
132  } catch (e) {
133    console.error('❌ Failed to initialize Dumber toast system:', e);
134  }
135})();`
136}

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