v0.8.0 0fa6785

toast.ts RawHistoryFolder

1/**
2 * Toast System Module
3 *
4 * Provides toast notification functionality using Svelte 5 components
5 * with proper DOM ready checking and theme support.
6 */
7
8import { mount, flushSync } from 'svelte';
9import ToastContainer from '$components/toast/ToastContainer.svelte';
10import { ensureShadowMount } from './shadowHost';
11
12export interface ToastConfig {
13  theme?: 'light' | 'dark';
14}
15
16// Type definitions for window globals
17declare global {
18  interface Window {
19    __dumber_toast_loaded?: boolean;
20    __dumber_initial_theme?: string;
21  }
22}
23
24let toastInitialized = false;
25
26// Apply theme based on current system/GTK preference
27function updateTheme(isDark: boolean): void {
28  if (isDark) {
29    document.documentElement.classList.add('dark');
30  } else {
31    document.documentElement.classList.remove('dark');
32  }
33}
34
35export async function initializeToast(config?: ToastConfig): Promise<void> {
36  if (toastInitialized || window.__dumber_toast_loaded) {
37    // Verify functions still exist - if not, reinitialize
38    if (typeof window.__dumber_showToast === 'function' &&
39        typeof window.__dumber_showZoomToast === 'function') {
40      console.log('✅ Toast system already initialized and functional');
41      return;
42    }
43    // If functions are missing, reinitialize
44    console.log('⚠️ Toast functions missing, reinitializing...');
45    toastInitialized = false;
46    window.__dumber_toast_loaded = false;
47  }
48
49  console.log('🚀 Initializing Svelte toast system at:', document.readyState);
50  console.log('📊 DOM state check - head:', !!document.head, 'body:', !!document.body);
51  window.__dumber_toast_loaded = true;
52
53  // Ensure DOM is ready
54  if (document.readyState === 'loading') {
55    return new Promise((resolve) => {
56      document.addEventListener('DOMContentLoaded', async () => {
57        await initializeToast(config);
58        resolve();
59      });
60    });
61  }
62
63  if (!document.head || !document.body) {
64    console.warn('❌ DOM structure not ready, deferring toast initialization. Retrying...');
65    window.__dumber_toast_loaded = false; // Reset the flag so we can try again
66    return new Promise((resolve) => {
67      const checkDOM = () => {
68        if (document.head && document.body) {
69          console.log('✅ DOM structure now ready, initializing toast system');
70          initializeToast(config).then(() => resolve());
71        } else {
72          console.log('⏳ DOM still not ready, retrying in 50ms...');
73          setTimeout(checkDOM, 50);
74        }
75      };
76      setTimeout(checkDOM, 100);
77    });
78  }
79
80  try {
81    // Use the global Shadow DOM host for isolation
82    const rootElement = ensureShadowMount('dumber-toast');
83
84    // Initialize theme
85    const initialTheme = config?.theme || window.__dumber_initial_theme;
86    if (initialTheme !== undefined) {
87      updateTheme(initialTheme === 'dark');
88    }
89
90    // Mount the Svelte toast container (Svelte 5 syntax)
91    mount(ToastContainer, {
92      target: rootElement as unknown as Element
93    });
94
95    // Signal isolated GUI readiness for page-world bridge
96    try {
97      document.documentElement.setAttribute('data-dumber-gui', 'ready');
98      document.dispatchEvent(new CustomEvent('dumber:gui-ready'));
99    } catch {
100      // Ignore errors in GUI readiness signaling
101    }
102
103    // Listen for page-world bridge events and forward to internal toast functions
104    const handleShowToast = (e: Event) => {
105      const detail = (e as CustomEvent).detail || {};
106      if (typeof window.__dumber_showToast === 'function') {
107        window.__dumber_showToast(detail.message, detail.duration, detail.type);
108      }
109    };
110    document.addEventListener('dumber:showToast', handleShowToast);
111    document.addEventListener('dumber:toast:show', handleShowToast);
112
113    const handleZoomToast = (e: Event) => {
114      const detail = (e as CustomEvent).detail || {};
115      if (typeof window.__dumber_showZoomToast === 'function') {
116        window.__dumber_showZoomToast(detail.level);
117      }
118    };
119    document.addEventListener('dumber:showZoomToast', handleZoomToast);
120    document.addEventListener('dumber:toast:zoom', handleZoomToast);
121
122    // Force immediate effect execution to ensure onMount callbacks run
123    flushSync();
124
125    // Toast functions are now exposed by the component itself
126    console.log('✅ Toast functions exposed via component instantiation');
127
128    toastInitialized = true;
129    console.log('✅ Toast system initialized with Svelte 5');
130
131  } catch (error) {
132    console.error('❌ Failed to initialize toast system:', error);
133
134    // Fallback to basic toast system if Svelte fails
135    initializeFallbackToast();
136  }
137}
138
139function exposeToastFunctions(): void {
140  // The Svelte ToastContainer component already exposes these functions
141  // We just need to make sure they exist, they're handled by the component
142  console.log('✅ Toast functions will be exposed by ToastContainer component');
143}
144
145function initializeFallbackToast(): void {
146  console.warn('🔄 Falling back to basic toast system');
147
148  // Create basic toast container for fallback
149  const fallbackContainer = document.createElement('div');
150  fallbackContainer.id = 'dumber-fallback-toast';
151  fallbackContainer.style.cssText = `
152    position: fixed;
153    top: 20px;
154    right: 20px;
155    z-index: 10000;
156    pointer-events: none;
157  `;
158  document.documentElement.appendChild(fallbackContainer);
159
160  // Expose toast functions for fallback too
161  exposeToastFunctions();
162
163  toastInitialized = true;
164  console.log('✅ Fallback toast system ready');
165}

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