feat(chat): add one-click Plaza publish for HTML page messages.

Speed up the quick-plaza path with session snapshot reads, publish timeouts, and a modal fix so parent re-renders no longer abort in-flight requests.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-07-05 19:28:36 +08:00
parent 53eb0014ba
commit 3318c04490
11 changed files with 766 additions and 140 deletions
+70 -52
View File
@@ -1014,45 +1014,57 @@ export function useTKMindChat(
setPendingTool(null);
activeRequestId.current = null;
const knownSession = sessionsRef.current.find((s) => s.id === sessionId);
const hints = knownSession
? { messageCount: knownSession.message_count, updatedAt: knownSession.updated_at }
: undefined;
const detailPromise = withTransientConnectRetry(() =>
loadSessionDetail(sessionId, hints, {
before: 0,
limit: appConfig.sessionMessagePageSize,
}),
);
const resumedPromise =
options?.skipResume || isDirectChatSessionId(sessionId)
? Promise.resolve(options.seedSession ?? null)
: withTransientConnectRetry(() =>
resumeSession(sessionId, {
skipReconcile: options?.skipReconcile ?? false,
}),
);
const { session: detail, messages: history, page } = await detailPromise;
if (token !== connectTokenRef.current) return;
let completed = false;
try {
const knownSession = sessionsRef.current.find((s) => s.id === sessionId);
const hints = knownSession
? { messageCount: knownSession.message_count, updatedAt: knownSession.updated_at }
: undefined;
const detailPromise = withTransientConnectRetry(() =>
loadSessionDetail(sessionId, hints, {
before: 0,
limit: appConfig.sessionMessagePageSize,
}),
);
const resumedPromise =
options?.skipResume || isDirectChatSessionId(sessionId)
? Promise.resolve(options.seedSession ?? null)
: withTransientConnectRetry(() =>
resumeSession(sessionId, {
skipReconcile: options?.skipReconcile ?? false,
}),
);
const { session: detail, messages: history, page } = await detailPromise;
if (token !== connectTokenRef.current) return;
writeStoredSessionId(userRef.current?.id, sessionId);
setSession((current) => (current?.id === sessionId ? { ...detail, ...current, id: sessionId } : { ...detail, id: sessionId }));
messagesRef.current = history;
messageHistoryLoadedCountRef.current = history.length;
messageHistoryTotalRef.current = Math.max(Number(page.total ?? history.length), history.length);
messageHistoryHasMoreRef.current = history.length < messageHistoryTotalRef.current;
setMessageHistoryHasMore(messageHistoryHasMoreRef.current);
setMessageHistoryTotal(messageHistoryTotalRef.current);
setMessages(history);
const resumed = await resumedPromise;
if (token !== connectTokenRef.current) return;
setSession({ ...detail, ...(resumed ?? {}), id: sessionId });
setChatState('idle');
setSessions((prev) =>
prependUnique(prev, toSessionSummary({ ...detail, ...(resumed ?? {}), id: sessionId })),
);
writeStoredSessionId(userRef.current?.id, sessionId);
setSession((current) =>
current?.id === sessionId ? { ...detail, ...current, id: sessionId } : { ...detail, id: sessionId },
);
messagesRef.current = history;
messageHistoryLoadedCountRef.current = history.length;
messageHistoryTotalRef.current = Math.max(Number(page.total ?? history.length), history.length);
messageHistoryHasMoreRef.current = history.length < messageHistoryTotalRef.current;
setMessageHistoryHasMore(messageHistoryHasMoreRef.current);
setMessageHistoryTotal(messageHistoryTotalRef.current);
setMessages(history);
const resumed = await resumedPromise;
if (token !== connectTokenRef.current) return;
setSession({ ...detail, ...(resumed ?? {}), id: sessionId });
setChatState('idle');
completed = true;
setSessions((prev) =>
prependUnique(prev, toSessionSummary({ ...detail, ...(resumed ?? {}), id: sessionId })),
);
subscribeToSession(sessionId);
subscribeToSession(sessionId);
} finally {
if (!completed && token === connectTokenRef.current) {
setChatState((current) =>
current === 'connecting' || current === 'loading' ? 'idle' : current,
);
}
}
},
[clearActiveRequestMissingTimer, subscribeToSession],
);
@@ -1528,22 +1540,23 @@ export function useTKMindChat(
setPendingTool(null);
setChatState('connecting');
if (
previousSessionId &&
previousSession &&
shouldShowNewChatTitle(toSessionSummary(previousSession))
) {
try {
await deleteChatSession(previousSessionId);
if (token === connectTokenRef.current) {
setSessions((prev) => prev.filter((item) => item.id !== previousSessionId));
}
} catch {
// Keep the abandoned empty session in history if cleanup fails.
}
}
let completed = false;
try {
if (
previousSessionId &&
previousSession &&
shouldShowNewChatTitle(toSessionSummary(previousSession))
) {
try {
await deleteChatSession(previousSessionId);
if (token === connectTokenRef.current) {
setSessions((prev) => prev.filter((item) => item.id !== previousSessionId));
}
} catch {
// Keep the abandoned empty session in history if cleanup fails.
}
}
const started = await startSession();
if (token !== connectTokenRef.current) return;
@@ -1560,12 +1573,17 @@ export function useTKMindChat(
if (token !== connectTokenRef.current) return;
subscribeToSession(nextSession.id);
setChatState('idle');
completed = true;
void refreshSessions();
} catch (err) {
if (token !== connectTokenRef.current) return;
setSession(null);
setChatState('idle');
setError(err instanceof Error ? err.message : String(err));
} finally {
if (!completed && token === connectTokenRef.current) {
setChatState((current) => (current === 'connecting' ? 'idle' : current));
}
}
}, [
clearActiveRequestMissingTimer,