@@ -35,15 +35,22 @@ impl UiState {
3535 notifications: [],
3636 nextId: 1,
3737 addNotification(type, message, duration = null) {
38+ // Check if this exact message already exists
39+ const exists = this.notifications.some(n => n.message === message && n.type === type);
40+ if (exists) {
41+ return; // Don't add duplicate
42+ }
43+
3844 const id = this.nextId++;
3945 this.notifications.push({ id, type, message });
4046
41- // Auto-dismiss success and info notifications
42- if (duration !== null || (type === 'success' || type === 'info')) {
43- setTimeout(() => {
44- this.removeNotification(id);
45- }, duration || 4000);
46- }
47+ // Auto-dismiss all notification types
48+ const dismissTime = duration !== null ? duration :
49+ type === 'error' ? 8000 :
50+ 4000;
51+ setTimeout(() => {
52+ this.removeNotification(id);
53+ }, dismissTime);
4754 },
4855 removeNotification(id) {
4956 this.notifications = this.notifications.filter(n => n.id !== id);
@@ -53,9 +60,11 @@ impl UiState {
5360 }
5461 }"#
5562 "@ajax:error.window" =r#"
56- const status = $event.detail.xhr?.status;
63+ console.log('AJAX error event:', $event.detail);
64+ const xhr = $event.detail.xhr || $event.detail;
65+ const status = xhr?.status;
5766 let message;
58- if (status === 0) {
67+ if (status === 0 || status === undefined ) {
5968 message = '⚠ Network Error - Unable to complete request';
6069 } else if (status >= 500) {
6170 message = '⚠ Server Error (' + status + ')';
@@ -109,7 +118,19 @@ pub(crate) fn render_html_footer() -> Markup {
109118 img.removeAttribute("loading");
110119 }
111120 });
121+ });
112122
123+ // Catch unhandled promise rejections (network errors)
124+ window.addEventListener('unhandledrejection', (event) => {
125+ console.log('Unhandled rejection:', event);
126+ if (event.reason instanceof TypeError &&
127+ (event.reason.message.includes('NetworkError') ||
128+ event.reason.message.includes('fetch'))) {
129+ window.dispatchEvent(new CustomEvent('notify', {
130+ detail: { type: 'error', message: '⚠ Network Error - Unable to complete request' }
131+ }));
132+ event.preventDefault();
133+ }
113134 });
114135 "# ) )
115136 }
@@ -162,12 +183,12 @@ pub(crate) fn render_html_footer() -> Markup {
162183 };
163184
164185 this.ws.onerror = (error) => {
165- console. error(" WebSocket error:", error);
186+ // Suppress error logging - it's expected if WebSocket isn't available
166187 };
167188
168189 this.ws.onclose = () => {
169- console.log(" WebSocket closed, reconnecting...");
170- setTimeout(() => this.connect(url), 1000);
190+ // Don't reconnect - WebSocket is optional
191+ // If we want reconnection, it should be with exponential backoff
171192 };
172193 },
173194 destroy() {
0 commit comments