surface session toast notifications
This commit is contained in:
@@ -1088,12 +1088,10 @@ const App: Component = () => {
|
|||||||
|
|
||||||
<Toaster
|
<Toaster
|
||||||
position="top-right"
|
position="top-right"
|
||||||
gutter={12}
|
gutter={16}
|
||||||
toastOptions={{
|
toastOptions={{
|
||||||
duration: 5000,
|
duration: 8000,
|
||||||
className: `text-sm shadow-lg border border-base ${
|
className: "bg-transparent border-none shadow-none p-0",
|
||||||
isDark() ? "bg-surface-secondary text-primary" : "bg-white text-gray-900"
|
|
||||||
}`,
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,41 +9,53 @@ export type ToastPayload = {
|
|||||||
duration?: number
|
duration?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const variantAccent: Record<ToastVariant, { badge: string; border: string; text: string }> = {
|
const variantAccent: Record<
|
||||||
|
ToastVariant,
|
||||||
|
{
|
||||||
|
badge: string
|
||||||
|
container: string
|
||||||
|
headline: string
|
||||||
|
body: string
|
||||||
|
}
|
||||||
|
> = {
|
||||||
info: {
|
info: {
|
||||||
badge: "bg-blue-500",
|
badge: "bg-sky-500/40",
|
||||||
border: "border-blue-500/40",
|
container: "bg-slate-900/95 border-slate-700 text-slate-100",
|
||||||
text: "text-blue-100",
|
headline: "text-slate-50",
|
||||||
|
body: "text-slate-200/80",
|
||||||
},
|
},
|
||||||
success: {
|
success: {
|
||||||
badge: "bg-emerald-500",
|
badge: "bg-emerald-500/40",
|
||||||
border: "border-emerald-500/40",
|
container: "bg-emerald-950/90 border-emerald-800 text-emerald-50",
|
||||||
text: "text-emerald-100",
|
headline: "text-emerald-50",
|
||||||
|
body: "text-emerald-100/80",
|
||||||
},
|
},
|
||||||
warning: {
|
warning: {
|
||||||
badge: "bg-amber-500",
|
badge: "bg-amber-500/40",
|
||||||
border: "border-amber-500/40",
|
container: "bg-amber-950/90 border-amber-800 text-amber-50",
|
||||||
text: "text-amber-100",
|
headline: "text-amber-50",
|
||||||
|
body: "text-amber-100/80",
|
||||||
},
|
},
|
||||||
error: {
|
error: {
|
||||||
badge: "bg-rose-500",
|
badge: "bg-rose-500/40",
|
||||||
border: "border-rose-500/40",
|
container: "bg-rose-950/90 border-rose-800 text-rose-50",
|
||||||
text: "text-rose-100",
|
headline: "text-rose-50",
|
||||||
|
body: "text-rose-100/80",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export function showToastNotification(payload: ToastPayload) {
|
export function showToastNotification(payload: ToastPayload) {
|
||||||
const accent = variantAccent[payload.variant]
|
const accent = variantAccent[payload.variant]
|
||||||
const duration = payload.duration ?? 5000
|
const duration = payload.duration ?? 10000
|
||||||
|
|
||||||
toast.custom(
|
toast.custom(
|
||||||
() => (
|
() => (
|
||||||
<div class={`min-w-[280px] max-w-[360px] rounded-xl border px-4 py-3 shadow-xl bg-surface-secondary ${accent.border}`}>
|
<div class={`pointer-events-auto w-[320px] max-w-[360px] rounded-lg border px-4 py-3 shadow-xl ${accent.container}`}>
|
||||||
<div class="flex gap-3">
|
<div class="flex items-start gap-3">
|
||||||
<span class={`mt-1 inline-block h-2.5 w-2.5 rounded-full ${accent.badge}`} />
|
<span class={`mt-1 inline-block h-2.5 w-2.5 rounded-full ${accent.badge}`} />
|
||||||
<div class="flex-1 text-sm leading-snug">
|
<div class="flex-1 text-sm leading-snug">
|
||||||
{payload.title && <p class="font-semibold text-primary">{payload.title}</p>}
|
{payload.title && <p class={`font-semibold ${accent.headline}`}>{payload.title}</p>}
|
||||||
<p class={`text-primary/90 ${payload.title ? "mt-1" : ""}`}>{payload.message}</p>
|
<p class={`${accent.body} ${payload.title ? "mt-1" : ""}`}>{payload.message}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -30,6 +30,13 @@ interface TuiToastEvent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface SessionIdleEvent {
|
||||||
|
type: "session.idle"
|
||||||
|
properties: {
|
||||||
|
sessionID: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const [connectionStatus, setConnectionStatus] = createSignal<
|
const [connectionStatus, setConnectionStatus] = createSignal<
|
||||||
Map<string, "connecting" | "connected" | "disconnected" | "error">
|
Map<string, "connecting" | "connected" | "disconnected" | "error">
|
||||||
>(new Map())
|
>(new Map())
|
||||||
@@ -118,7 +125,7 @@ class SSEManager {
|
|||||||
this.onTuiToast?.(instanceId, event as TuiToastEvent)
|
this.onTuiToast?.(instanceId, event as TuiToastEvent)
|
||||||
break
|
break
|
||||||
case "session.idle":
|
case "session.idle":
|
||||||
console.log("[SSE] Session idle")
|
this.onSessionIdle?.(instanceId, event as SessionIdleEvent)
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
console.warn("[SSE] Unknown event type:", event.type)
|
console.warn("[SSE] Unknown event type:", event.type)
|
||||||
@@ -161,6 +168,7 @@ class SSEManager {
|
|||||||
onSessionCompacted?: (instanceId: string, event: any) => void
|
onSessionCompacted?: (instanceId: string, event: any) => void
|
||||||
onSessionError?: (instanceId: string, event: any) => void
|
onSessionError?: (instanceId: string, event: any) => void
|
||||||
onTuiToast?: (instanceId: string, event: TuiToastEvent) => void
|
onTuiToast?: (instanceId: string, event: TuiToastEvent) => void
|
||||||
|
onSessionIdle?: (instanceId: string, event: SessionIdleEvent) => void
|
||||||
|
|
||||||
getStatus(instanceId: string): "connecting" | "connected" | "disconnected" | "error" | null {
|
getStatus(instanceId: string): "connecting" | "connected" | "disconnected" | "error" | null {
|
||||||
return connectionStatus().get(instanceId) ?? null
|
return connectionStatus().get(instanceId) ?? null
|
||||||
|
|||||||
@@ -1767,6 +1767,19 @@ function handleSessionCompacted(instanceId: string, event: any): void {
|
|||||||
|
|
||||||
console.log(`[SSE] Session compacted: ${sessionID}`)
|
console.log(`[SSE] Session compacted: ${sessionID}`)
|
||||||
loadMessages(instanceId, sessionID, true).catch(console.error)
|
loadMessages(instanceId, sessionID, true).catch(console.error)
|
||||||
|
|
||||||
|
const instanceSessions = sessions().get(instanceId)
|
||||||
|
const session = instanceSessions?.get(sessionID)
|
||||||
|
const label = session?.title?.trim() ? session.title : sessionID
|
||||||
|
const instanceFolder = instances().get(instanceId)?.folder ?? instanceId
|
||||||
|
const instanceName = instanceFolder.split(/[\\/]/).filter(Boolean).pop() ?? instanceFolder
|
||||||
|
|
||||||
|
showToastNotification({
|
||||||
|
title: instanceName,
|
||||||
|
message: `Session ${label ? `"${label}"` : sessionID} was compacted`,
|
||||||
|
variant: "info",
|
||||||
|
duration: 10000,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSessionError(instanceId: string, event: any): void {
|
function handleSessionError(instanceId: string, event: any): void {
|
||||||
@@ -1821,6 +1834,24 @@ function handleTuiToast(_instanceId: string, event: any): void {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleSessionIdle(instanceId: string, event: any): void {
|
||||||
|
const sessionID = event.properties?.sessionID
|
||||||
|
if (!sessionID) return
|
||||||
|
|
||||||
|
const instanceSessions = sessions().get(instanceId)
|
||||||
|
const session = instanceSessions?.get(sessionID)
|
||||||
|
const label = session?.title?.trim() ? session.title : sessionID
|
||||||
|
const instanceFolder = instances().get(instanceId)?.folder ?? instanceId
|
||||||
|
const instanceName = instanceFolder.split(/[\\/]/).filter(Boolean).pop() ?? instanceFolder
|
||||||
|
|
||||||
|
showToastNotification({
|
||||||
|
title: instanceName,
|
||||||
|
message: `Session ${label ? `"${label}"` : sessionID} is idle`,
|
||||||
|
variant: "info",
|
||||||
|
duration: 10000,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
sseManager.onMessageUpdate = handleMessageUpdate
|
sseManager.onMessageUpdate = handleMessageUpdate
|
||||||
sseManager.onMessageRemoved = handleMessageRemoved
|
sseManager.onMessageRemoved = handleMessageRemoved
|
||||||
sseManager.onMessagePartRemoved = handleMessagePartRemoved
|
sseManager.onMessagePartRemoved = handleMessagePartRemoved
|
||||||
@@ -1828,6 +1859,7 @@ sseManager.onSessionUpdate = handleSessionUpdate
|
|||||||
sseManager.onSessionCompacted = handleSessionCompacted
|
sseManager.onSessionCompacted = handleSessionCompacted
|
||||||
sseManager.onSessionError = handleSessionError
|
sseManager.onSessionError = handleSessionError
|
||||||
sseManager.onTuiToast = handleTuiToast
|
sseManager.onTuiToast = handleTuiToast
|
||||||
|
sseManager.onSessionIdle = handleSessionIdle
|
||||||
|
|
||||||
export {
|
export {
|
||||||
sessions,
|
sessions,
|
||||||
|
|||||||
Reference in New Issue
Block a user