v0.3.0 d226234

notifications.ts RawHistoryFolder

1import type { NotificationOptions } from '../types/app.js';
2
3export class NotificationService {
4  private zoomNotification: HTMLElement | null = null;
5  private generalNotification: HTMLElement | null = null;
6
7  showZoom(message: string, duration = 2000): void {
8    // Create or update zoom notification
9    if (!this.zoomNotification) {
10      this.zoomNotification = this.createNotificationElement('zoom-notification', {
11        top: '20px',
12        right: '20px',
13        background: 'rgba(0, 0, 0, 0.8)',
14        color: 'white'
15      });
16    }
17
18    this.updateNotification(this.zoomNotification, message, duration);
19  }
20
21  show(message: string, type: NotificationOptions['type'] = 'info', duration = 3000): void {
22    // Create or update general notification
23    if (!this.generalNotification) {
24      this.generalNotification = this.createNotificationElement('general-notification', {
25        top: '60px',
26        right: '20px'
27      });
28    }
29
30    // Set colors based on type
31    const colors = this.getNotificationColors(type);
32    this.generalNotification.style.background = colors.background;
33    this.generalNotification.style.color = colors.color;
34
35    this.updateNotification(this.generalNotification, message, duration);
36  }
37
38  private createNotificationElement(id: string, baseStyles: Record<string, string>): HTMLElement {
39    const notification = document.createElement('div');
40    notification.id = id;
41    
42    const styles = {
43      position: 'fixed',
44      padding: '10px 15px',
45      borderRadius: '5px',
46      fontSize: '14px',
47      zIndex: '10000',
48      transition: 'opacity 0.3s',
49      opacity: '0',
50      ...baseStyles
51    };
52
53    notification.style.cssText = Object.entries(styles)
54      .map(([key, value]) => `${this.camelToKebab(key)}: ${value}`)
55      .join('; ');
56
57    document.body.appendChild(notification);
58    return notification;
59  }
60
61  private updateNotification(element: HTMLElement, message: string, duration: number): void {
62    element.textContent = message;
63    element.style.opacity = '1';
64
65    // Clear any existing timeout
66    const existingTimeout = (element as any).timeout;
67    if (existingTimeout) {
68      clearTimeout(existingTimeout);
69    }
70
71    // Hide after duration
72    (element as any).timeout = setTimeout(() => {
73      element.style.opacity = '0';
74    }, duration);
75  }
76
77  private getNotificationColors(type: NotificationOptions['type']): { background: string; color: string } {
78    switch (type) {
79      case 'error':
80        return { background: 'rgba(220, 53, 69, 0.9)', color: 'white' };
81      case 'success':
82        return { background: 'rgba(25, 135, 84, 0.9)', color: 'white' };
83      default:
84        return { background: 'rgba(13, 110, 253, 0.9)', color: 'white' };
85    }
86  }
87
88  private camelToKebab(str: string): string {
89    return str.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`);
90  }
91}

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