feat(webui): refactor chat page onto global client and Signal Deck
This commit is contained in:
parent
14ca5dd7b4
commit
d9e3245031
@ -2,13 +2,12 @@
|
||||
import { onMount, tick } from "svelte";
|
||||
import { Tooltip } from "bits-ui";
|
||||
import { clientId, formatTime, randomId } from "../lib/api.js";
|
||||
import { chat } from "../lib/chat.svelte.js";
|
||||
import Markdown from "../lib/Markdown.svelte";
|
||||
import ToolCallCard from "../lib/ToolCallCard.svelte";
|
||||
import TurnView from "../lib/TurnView.svelte";
|
||||
|
||||
let { notify } = $props();
|
||||
let socket = $state(null);
|
||||
let connected = $state(false);
|
||||
let sessions = $state([]);
|
||||
let currentId = $state(null);
|
||||
let messages = $state([]);
|
||||
@ -27,8 +26,6 @@
|
||||
let todoOpen = $state(false);
|
||||
let messageBox;
|
||||
let input;
|
||||
let reconnectTimer;
|
||||
let stopped = false;
|
||||
const currentSession = $derived(sessions.find((item) => item.session_id === currentId));
|
||||
const currentPlan = $derived(currentId ? plansBySession[currentId] || null : null);
|
||||
const completedItems = $derived(currentPlan?.items?.filter((item) => item.status === "completed").length || 0);
|
||||
@ -52,28 +49,7 @@
|
||||
});
|
||||
|
||||
function send(frame) {
|
||||
if (socket?.readyState === WebSocket.OPEN) socket.send(JSON.stringify(frame));
|
||||
else notify("聊天连接尚未就绪", true);
|
||||
}
|
||||
|
||||
function connect() {
|
||||
const scheme = location.protocol === "https:" ? "wss" : "ws";
|
||||
const ws = new WebSocket(`${scheme}://${location.host}/ws?client_id=${encodeURIComponent(clientId())}`);
|
||||
socket = ws;
|
||||
ws.onopen = () => {
|
||||
connected = true;
|
||||
plansBySession = {};
|
||||
unseenPlanSessions = {};
|
||||
todoOpen = false;
|
||||
send({ type: "list_sessions", include_archived: false });
|
||||
send({ type: "get_slash_commands" });
|
||||
};
|
||||
ws.onerror = () => ws.close();
|
||||
ws.onclose = () => {
|
||||
connected = false;
|
||||
if (!stopped) reconnectTimer = setTimeout(connect, 1800);
|
||||
};
|
||||
ws.onmessage = (event) => handleFrame(JSON.parse(event.data));
|
||||
if (!chat.send(frame)) notify("聊天连接尚未就绪", true);
|
||||
}
|
||||
|
||||
function handleFrame(frame) {
|
||||
@ -218,7 +194,7 @@
|
||||
function submit() {
|
||||
const content = draft.trim();
|
||||
const ready = pendingUploads.filter((upload) => upload.status === "ready");
|
||||
if ((!content && !ready.length) || !connected || pendingUploads.some((upload) => upload.status === "uploading")) return;
|
||||
if ((!content && !ready.length) || !chat.connected || pendingUploads.some((upload) => upload.status === "uploading")) return;
|
||||
appendMessage("user", content, ready.map((upload, index) => ({
|
||||
index,
|
||||
name: upload.name,
|
||||
@ -258,7 +234,7 @@
|
||||
}
|
||||
|
||||
function uploadFile(file) {
|
||||
if (!connected) return notify("聊天连接尚未就绪", true);
|
||||
if (!chat.connected) return notify("聊天连接尚未就绪", true);
|
||||
const localId = randomId();
|
||||
const localUrl = file.type.startsWith("image/") ? URL.createObjectURL(file) : null;
|
||||
pendingUploads = [...pendingUploads, {
|
||||
@ -380,11 +356,20 @@
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
connect();
|
||||
const unsubscribe = chat.subscribe(handleFrame);
|
||||
const onOpen = (frame) => {
|
||||
if (frame.type !== "_open") return;
|
||||
plansBySession = {};
|
||||
unseenPlanSessions = {};
|
||||
todoOpen = false;
|
||||
send({ type: "list_sessions", include_archived: false });
|
||||
send({ type: "get_slash_commands" });
|
||||
};
|
||||
const unsubOpen = chat.subscribe(onOpen);
|
||||
if (chat.connected) onOpen({ type: "_open" });
|
||||
return () => {
|
||||
stopped = true;
|
||||
clearTimeout(reconnectTimer);
|
||||
socket?.close();
|
||||
unsubscribe();
|
||||
unsubOpen();
|
||||
clearPendingUploads();
|
||||
};
|
||||
});
|
||||
@ -515,8 +500,8 @@
|
||||
aria-activedescendant={commandSuggestions.length ? `slash-command-${selectedCommand}` : undefined}
|
||||
placeholder="输入消息,输入 / 查看命令"
|
||||
></textarea>
|
||||
<button class="send" type="submit" aria-label="发送" disabled={!connected || (!draft.trim() && !pendingUploads.some((upload) => upload.status === "ready")) || pendingUploads.some((upload) => upload.status === "uploading")}>↑</button>
|
||||
<small><span class:online={connected}>{connected ? "已连接" : "已断开,正在重连"}</span><span>/ 打开命令 · Shift+Enter 换行</span></small>
|
||||
<button class="send" type="submit" aria-label="发送" disabled={!chat.connected || (!draft.trim() && !pendingUploads.some((upload) => upload.status === "ready")) || pendingUploads.some((upload) => upload.status === "uploading")}>↑</button>
|
||||
<small><span class:online={chat.connected}>{chat.connected ? "已连接" : "已断开,正在重连"}</span><span>/ 打开命令 · Shift+Enter 换行</span></small>
|
||||
</form>
|
||||
</div>
|
||||
{#if currentPlan}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user