This commit is contained in:
hupenglong1
2026-05-22 19:48:47 +08:00
parent 20690cdef3
commit 5311e6d97c
31 changed files with 4620 additions and 472 deletions
@@ -273,7 +273,25 @@ function useRefreshCurrentRun() {
const runStatus = await fetchLatestRunStatus(sessionId);
if (cancelled) return;
if (runStatus?.status === "queued" || runStatus?.status === "running") {
activeRunRef.current = runStatus.run_id ?? "active";
const runKey = runStatus.run_id ?? "active";
const previousRunKey = activeRunRef.current;
activeRunRef.current = runKey;
// Watcher auto-resume: backend started a new run via _maybe_auto_resume
// without a local SSE stream driving the UI. We need to replay once so
// the active-run placeholder (pending prompt + 思考中 bubble) appears.
// useRefreshReplayedRun will take over polling once replayedRun.active
// becomes true.
if (!runtimeRunning && previousRunKey !== runKey) {
const response = await fetch(`/api/claw/sessions/${sessionId}`, {
cache: "no-store",
});
if (!response.ok || cancelled) return;
const payload = await response.json();
replaySession(
sessionId,
toReplayRepository(payload, sessionId, runStatus),
);
}
return;
}
if (!runtimeRunning && !activeRunRef.current) return;
@@ -632,6 +650,7 @@ function WorkspaceSwitchDialog({
if (needsFirstMessageSession) {
writePendingWorkspaceSessionId(sessionIdForRequest);
}
window.dispatchEvent(new Event("claw-workspaces-changed"));
setStatus(payload as JupyterWorkspaceStatus);
setProgress({ percent: 100, label: "切换完成" });
setMessage("工作区切换成功,后续工具会在远端 Jupyter 工作区执行。");
@@ -1186,6 +1205,7 @@ function ModelPickerButton({
};
}) {
const [query, setQuery] = useState("");
const [open, setOpen] = useState(false);
const normalizedQuery = query.trim().toLowerCase();
const filteredModels = useMemo(() => {
if (!normalizedQuery) return status.models;
@@ -1199,7 +1219,7 @@ function ModelPickerButton({
}, [normalizedQuery, status.models]);
return (
<PopoverPrimitive.Root>
<PopoverPrimitive.Root open={open} onOpenChange={setOpen}>
<PopoverPrimitive.Trigger asChild>
<button
type="button"
@@ -1240,7 +1260,10 @@ function ModelPickerButton({
key={model.id}
type="button"
className="flex h-8 w-full items-center justify-between gap-2 rounded-lg px-2 text-left text-sm hover:bg-muted"
onClick={() => status.setModel(model.id)}
onClick={() => {
setOpen(false);
void status.setModel(model.id);
}}
>
<span className="min-w-0 flex-1 truncate">{model.id}</span>
<span className="shrink-0 text-muted-foreground text-xs">