v0.14.0 76d1892

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  // Listen for toast events from main world bridge
88  if (typeof document !== 'undefined') {
89    // Listen for general toast requests
90    document.addEventListener('dumber:toast', ((event: CustomEvent) => {
91      const { message, duration, type } = event.detail;
92      console.log('[ToastContainer] Received toast event:', { message, duration, type });
93      addToast(message, duration, type);
94    }) as EventListener);
95
96    // Listen for zoom toast requests
97    document.addEventListener('dumber:toast:zoom', ((event: CustomEvent) => {
98      const { level } = event.detail;
99      console.log('[ToastContainer] Received zoom toast event:', level);
100      showZoomToast(level);
101    }) as EventListener);
102
103    console.log('✅ Toast event listeners set up for CustomEvents');
104  }
105
106  // Effect that runs when toasts array changes
107  $effect(() => {
108    const container = document.querySelector('.toast-container');
109    const browserRoot = document.querySelector('.browser-component-root');
110    console.log('[dumber] Toast state changed - container:', !!container, 'browser-root:', !!browserRoot, 'toasts:', toasts.length, 'toasts:', toasts.map(t => t.message));
111  });
112
113  // One-time setup effect
114  $effect(() => {
115    console.log('🔄 ToastContainer initialized, functions exposed');
116
117    // Monitor container periodically for debugging (disabled)
118    // const monitorInterval = setInterval(() => {
119    //   const container = document.querySelector('.toast-container');
120    //   const browserRoot = document.querySelector('.browser-component-root');
121    //   console.log('[dumber] Periodic check - container:', !!container, 'browser-root:', !!browserRoot, 'toasts:', toasts.length);
122    // }, 5000);
123
124    return () => {
125      // clearInterval(monitorInterval);
126      console.log('🗑️ ToastContainer cleanup');
127    };
128  });
129</script>
130
131<div class="browser-component-root toast-container">
132  {#each toasts as toast (toast.id)}
133    <Toast
134      id={toast.id}
135      message={toast.message}
136      duration={toast.duration}
137      type={toast.type}
138      onDismiss={dismissToast}
139    />
140  {/each}
141</div>
142
143<style>
144  .toast-container {
145    display: flex;
146    flex-direction: column;
147    gap: 0.5rem;
148    left: 0.5rem;
149    max-width: 20rem;
150    pointer-events: none;
151    position: fixed;
152    top: 0.5rem;
153    z-index: 2147483647;
154  }
155</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