fix(ui): a stream that went quiet reconnects, the rail says so, the panel opens on the master
A phone that slept brings back an SSE socket that never errored and never delivers: the panel read as live for hours while showing nothing new. Every chunk now marks the connection alive and a watchdog drops the socket after 40s of silence - twice the server's keepalive - so the loop reconnects. Each attempt gets its own controller, which is what lets the watchdog (and a manual refresh) end one connection without ending the loop. The connection is admitted in one dot beside the rail's search box, tapped to reconnect at once rather than waiting out the backoff - the pill that used to say this was dropped in the redesign and the panel had no way to own up to being stale. With nothing picked, the shell lands on the freshest open master instead of a prompt to pick something: what a phone wants when the panel opens. An explicit pick, and following the active note, still win.
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
import SearchIcon from "@lucide/svelte/icons/search";
|
||||
import { onMount } from "svelte";
|
||||
import type { ApiClient } from "$lib/api/client";
|
||||
import type { LiveState } from "$lib/api/live.svelte";
|
||||
import type {
|
||||
ConversationSummary,
|
||||
SearchFile,
|
||||
@@ -40,6 +41,23 @@
|
||||
const DAYS_SHOWN = 14;
|
||||
const TICK_MS = 30_000;
|
||||
|
||||
// The connection, said in one dot beside the search box: the panel can sit
|
||||
// untouched for hours on a phone, and this is where it admits it went quiet.
|
||||
const LIVE_DOT: Record<LiveState, string> = {
|
||||
connecting: "bg-warn",
|
||||
failed: "bg-destructive",
|
||||
off: "bg-muted-foreground/40",
|
||||
open: "bg-ok",
|
||||
retrying: "bg-warn animate-pulse-dot",
|
||||
};
|
||||
const LIVE_LABEL: Record<LiveState, string> = {
|
||||
connecting: "Connecting",
|
||||
failed: "Offline",
|
||||
off: "Not connected",
|
||||
open: "Live",
|
||||
retrying: "Reconnecting",
|
||||
};
|
||||
|
||||
let query = $state("");
|
||||
let expanded = $state<Set<string>>(new Set());
|
||||
let remote = $state<SearchResponse | null>(null);
|
||||
@@ -173,6 +191,17 @@
|
||||
></span>
|
||||
{/if}
|
||||
</label>
|
||||
<button
|
||||
aria-label={LIVE_LABEL[index.live.state]}
|
||||
class="grid size-8 shrink-0 place-items-center rounded-md hover:bg-accent"
|
||||
onclick={() => index.reconnect()}
|
||||
title="{LIVE_LABEL[index.live.state]} — tap to reconnect"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class={cn("size-2 rounded-full", LIVE_DOT[index.live.state])}
|
||||
></span>
|
||||
</button>
|
||||
<button
|
||||
aria-label="New conversation"
|
||||
class="rule-word inline-flex size-8 items-center justify-center rounded-md hover:bg-accent"
|
||||
|
||||
Reference in New Issue
Block a user