tiletopia/src/components/Notifications.svelte
megaproxy 3c2f6b8640 Add M4 orchestration: broadcast, idle notifications, palette
tree.ts
- LeafNode gains broadcast?: boolean
- walkLeaves(root) generator; toggleBroadcast helper

ops.ts (PaneOps)
- toggleBroadcast, broadcastFrom, setActivePane, registerPaneId,
  notify; activeLeafId data field.

XtermPane.svelte
- onSpawn(paneId), onInput(b64), onDataReceived(),
  and focusTrigger prop. All optional; backward-compatible.

LeafPane.svelte
- 📡 broadcast toggle; 5s idle detection -> ops.notify (once per
  idle cycle); active + broadcasting border colors; click-to-focus
  via setActivePane + focusTrigger bump.

New Notifications.svelte
- Top-right toast stack, slide-in, 5s auto-dismiss + click ×.

New Palette.svelte
- Modal overlay, backdrop, filtered leaf list with ↑/↓ + Enter,
  Escape to close.

App.svelte
- paneIdByLeaf Map for routing; notifications array + auto-dismiss;
  activeLeafId; Ctrl+K global listener; broadcastFrom routes via
  walkLeaves + writeToPane to all other broadcast leaves; ⌘K button
  in titlebar.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 13:08:40 +01:00

81 lines
1.6 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">
export interface Toast {
id: number;
message: string;
}
let {
notifications,
onDismiss,
}: {
notifications: Toast[];
onDismiss: (id: number) => void;
} = $props();
</script>
<div class="toast-stack">
{#each notifications as t (t.id)}
<div class="toast">
<span class="toast-msg">{t.message}</span>
<button
class="toast-x"
onclick={() => onDismiss(t.id)}
aria-label="Dismiss notification"
>×</button>
</div>
{/each}
</div>
<style>
.toast-stack {
position: fixed;
top: 36px;
right: 12px;
display: flex;
flex-direction: column;
gap: 6px;
z-index: 100;
pointer-events: none;
}
.toast {
pointer-events: auto;
display: flex;
align-items: center;
gap: 10px;
min-width: 220px;
max-width: 320px;
padding: 8px 10px 8px 14px;
background: #1f1f1f;
color: #ddd;
border: 1px solid #3a5a8c;
border-left-width: 3px;
border-radius: 4px;
font-size: 12px;
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45);
animation: slide-in 180ms ease-out;
}
.toast-msg {
flex: 1 1 auto;
line-height: 1.3;
word-break: break-word;
}
.toast-x {
flex: 0 0 auto;
background: transparent;
border: none;
color: #777;
font-size: 16px;
line-height: 1;
padding: 2px 6px;
cursor: pointer;
border-radius: 3px;
}
.toast-x:hover {
background: #2a2a2a;
color: #ddd;
}
@keyframes slide-in {
from { transform: translateX(20px); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
</style>