v0.14.0 76d1892

notifications.ts RawHistoryFolder

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

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