v0.10.0 0f4e9eb

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

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