v0.13.0 976176a

ToastContainer.svelte RawHistoryFolder

1<script lang="ts">
2  import Toast from './Toast.svelte';
3
4  interface ToastData {
5    id: number;
6    message: string;
7    duration?: number;
8    type?: 'info' | 'success' | 'error';
9  }
10
11  // Svelte 5 rune for reactive state
12  let toasts = $state<ToastData[]>([]);
13  let counter = $state(0);
14  let zoomToastId = $state<number | null>(null);
15  let zoomDebounceTimer: number | undefined = undefined;
16
17  function addToast(message: string, duration = 2500, type: 'info' | 'success' | 'error' = 'info'): number {
18    const id = ++counter;
19    const toast: ToastData = { id, message, duration, type };
20
21    toasts.push(toast);
22    console.log('📋 Toast:', message);
23    return id;
24  }
25
26  function dismissToast(id: number) {
27    console.log(`[ToastContainer] Dismissing toast with id: ${id}`);
28    const index = toasts.findIndex(t => t.id === id);
29    if (index === -1) {
30      console.log(`[ToastContainer] Toast with id ${id} not found in array`);
31      return;
32    }
33
34    // Clear zoom toast tracking if this is the zoom toast
35    if (zoomToastId === id) {
36      console.log(`[ToastContainer] Clearing zoom toast tracking for id: ${id}`);
37      zoomToastId = null;
38    }
39
40    console.log(`[ToastContainer] Removing toast at index ${index}, remaining: ${toasts.length - 1}`);
41    toasts.splice(index, 1);
42  }
43
44  function clearAllToasts() {
45    toasts = [];
46    zoomToastId = null;
47    if (zoomDebounceTimer) {
48      window.clearTimeout(zoomDebounceTimer);
49      zoomDebounceTimer = undefined;
50    }
51  }
52
53  function showZoomToast(zoomLevel: number) {
54    console.log('[dumber] showZoomToast called with level:', zoomLevel);
55
56    // Debug: Check if toast container exists in DOM
57    const container = document.querySelector('.toast-container');
58    const browserRoot = document.querySelector('.browser-component-root');
59    console.log('[dumber] DOM check - container exists:', !!container, 'browser-root exists:', !!browserRoot, 'toasts array length:', toasts.length);
60
61    // Clear existing debounce timer
62    if (zoomDebounceTimer) {
63      window.clearTimeout(zoomDebounceTimer);
64    }
65
66    // Remove existing zoom toasts immediately (no animation needed for replacement)
67    toasts = toasts.filter(toast => {
68      const isZoomToast = toast.message.includes('Zoom level:') || toast.id === zoomToastId;
69      return !isZoomToast;
70    });
71
72    // Show new zoom toast
73    const percentage = Math.round(zoomLevel * 100);
74    const message = `Zoom level: ${percentage}%`;
75    const id = addToast(message, 1500, 'info');
76    zoomToastId = id;
77
78    console.log('[dumber] Added zoom toast - id:', id, 'current toasts count:', toasts.length);
79
80    // Debounced cleanup for rapid zoom changes
81    zoomDebounceTimer = window.setTimeout(() => {
82      zoomToastId = null;
83      zoomDebounceTimer = undefined;
84    }, 1500);
85  }
86
87  // Expose API to global window for Go integration immediately
88  // This must happen outside onMount to ensure functions are available immediately
89  if (typeof window !== 'undefined') {
90    window.__dumber_showToast = addToast;
91    window.__dumber_dismissToast = dismissToast;
92    window.__dumber_clearToasts = clearAllToasts;
93    window.__dumber_showZoomToast = showZoomToast;
94    console.log('✅ Toast functions exposed to window:', {
95      showToast: typeof window.__dumber_showToast,
96      dismissToast: typeof window.__dumber_dismissToast,
97      clearToasts: typeof window.__dumber_clearToasts,
98      showZoomToast: typeof window.__dumber_showZoomToast
99    });
100  }
101
102  // Effect that runs when toasts array changes
103  $effect(() => {
104    const container = document.querySelector('.toast-container');
105    const browserRoot = document.querySelector('.browser-component-root');
106    console.log('[dumber] Toast state changed - container:', !!container, 'browser-root:', !!browserRoot, 'toasts:', toasts.length, 'toasts:', toasts.map(t => t.message));
107  });
108
109  // One-time setup effect
110  $effect(() => {
111    console.log('🔄 ToastContainer initialized, functions exposed');
112
113    // Monitor container periodically for debugging (disabled)
114    // const monitorInterval = setInterval(() => {
115    //   const container = document.querySelector('.toast-container');
116    //   const browserRoot = document.querySelector('.browser-component-root');
117    //   console.log('[dumber] Periodic check - container:', !!container, 'browser-root:', !!browserRoot, 'toasts:', toasts.length);
118    // }, 5000);
119
120    return () => {
121      // clearInterval(monitorInterval);
122      console.log('🗑️ ToastContainer cleanup');
123    };
124  });
125</script>
126
127<div class="browser-component-root toast-container">
128  {#each toasts as toast (toast.id)}
129    <Toast
130      id={toast.id}
131      message={toast.message}
132      duration={toast.duration}
133      type={toast.type}
134      onDismiss={dismissToast}
135    />
136  {/each}
137</div>
138
139<style>
140  .toast-container {
141    display: flex;
142    flex-direction: column;
143    gap: 0.5rem;
144    left: 0.5rem;
145    max-width: 20rem;
146    pointer-events: none;
147    position: fixed;
148    top: 0.5rem;
149    z-index: 2147483647;
150  }
151</style>

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