agent-orchestrator/ui-electrobun/src/mainview/ToastContainer.svelte
Hibryda 4ae558af17 feat(electrobun): fixes + 7 new features (terminal input, file browser, memory, toasts)
Fixes:
- Terminal accepts keyboard input (echo mode with prompt)
- Terminal drawer collapses properly (display:none preserves xterm state)

Features:
- 6 project tabs: Model | Docs | Context | Files | SSH | Memory
- FileBrowser.svelte: recursive tree with expand/collapse + file preview
- MemoryTab.svelte: memory cards with trust badges (human/agent/auto)
- Subagent tree in AgentPane (demo: search-agent, test-runner)
- Drag resize handle between agent pane and terminal
- Theme dropdown in Settings (4 Catppuccin flavors)
- ToastContainer.svelte: auto-dismiss notifications
2026-03-20 02:07:18 +01:00

132 lines
3.4 KiB
Svelte
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script lang="ts">
type ToastVariant = 'success' | 'warning' | 'error' | 'info';
interface Toast {
id: number;
message: string;
variant: ToastVariant;
dismissAt: number; // epoch ms
}
let toasts = $state<Toast[]>([]);
let counter = $state(0);
// Public API — call via component binding or module-level store
export function addToast(message: string, variant: ToastVariant = 'info', durationMs = 4000) {
const id = ++counter;
toasts = [...toasts, { id, message, variant, dismissAt: Date.now() + durationMs }];
setTimeout(() => dismiss(id), durationMs);
}
function dismiss(id: number) {
toasts = toasts.filter(t => t.id !== id);
}
// Show a demo toast on mount
import { onMount } from 'svelte';
onMount(() => {
setTimeout(() => {
addToast('Agent Orchestrator connected', 'success', 4000);
}, 800);
});
const ICONS: Record<ToastVariant, string> = {
success: '✓',
warning: '⚠',
error: '✕',
info: '',
};
</script>
<div class="toast-container" aria-live="polite" aria-label="Notifications">
{#each toasts as toast (toast.id)}
<div class="toast toast-{toast.variant}" role="alert">
<span class="toast-icon" aria-hidden="true">{ICONS[toast.variant]}</span>
<span class="toast-msg">{toast.message}</span>
<button
class="toast-close"
onclick={() => dismiss(toast.id)}
aria-label="Dismiss notification"
>×</button>
</div>
{/each}
</div>
<style>
.toast-container {
position: fixed;
bottom: 2.5rem; /* above status bar */
right: 0.875rem;
z-index: 400;
display: flex;
flex-direction: column;
gap: 0.375rem;
pointer-events: none;
}
.toast {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 0.625rem;
border-radius: 0.4375rem;
border: 1px solid var(--ctp-surface1);
background: var(--ctp-mantle);
font-size: 0.8125rem;
color: var(--ctp-text);
pointer-events: auto;
animation: toast-in 0.18s ease-out;
min-width: 14rem;
max-width: 22rem;
box-shadow: 0 0.5rem 1.5rem color-mix(in srgb, var(--ctp-crust) 70%, transparent);
}
@keyframes toast-in {
from { transform: translateX(1.5rem); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
.toast-success { border-left: 3px solid var(--ctp-green); }
.toast-warning { border-left: 3px solid var(--ctp-yellow); }
.toast-error { border-left: 3px solid var(--ctp-red); }
.toast-info { border-left: 3px solid var(--ctp-blue); }
.toast-icon {
flex-shrink: 0;
font-size: 0.75rem;
font-weight: 700;
}
.toast-success .toast-icon { color: var(--ctp-green); }
.toast-warning .toast-icon { color: var(--ctp-yellow); }
.toast-error .toast-icon { color: var(--ctp-red); }
.toast-info .toast-icon { color: var(--ctp-blue); }
.toast-msg {
flex: 1;
line-height: 1.4;
}
.toast-close {
flex-shrink: 0;
width: 1.25rem;
height: 1.25rem;
background: transparent;
border: none;
color: var(--ctp-overlay1);
font-size: 1rem;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
border-radius: 0.2rem;
padding: 0;
transition: color 0.1s, background 0.1s;
line-height: 1;
}
.toast-close:hover {
color: var(--ctp-text);
background: var(--ctp-surface0);
}
</style>